OrientDb how to not update links when using the object database - java

Hello I have an Object Database in OrientDb, I am currently opening and closing my connections to the database after getting the data I need, for this reason I am using a fetchplan *:-1 and detaching my pojos using .detachAll(entity, true).
For example:
public <T extends NamedEntity> List<T> findAll(Class<T> entityClass) {
db= pool.acquire(connectionString, user, password);
db.getEntityManager().registerEntityClasses("com.mypackage");
List<T> l= new ArrayList<T>();
OObjectIteratorClass<T> it= db.browseClass(entityClass);
if (it == null)
throw new RuntimeException("entityClass " + entityClass.getName() + "not browseable by OrientDb");
it.setFetchPlan("*:-1");
for (T entity : it) {
l.add((T) db.detachAll(entity, true));
}
db.close();
return l;
}
The problem is when I try to .save() these guys back to the database I get a version conflict if two of the entities both have links to a third entity. This happens because when I save the first one the link is also saved because of the detachAll call so when I save the second entity the link is also going to be saved again but this second entity link is in a version one below the current one (even though I did not touch the link at all.)
Like this:
List<MyClass> l= findAll(MyClass.class);
//assuming the list contains two elements
MyClass my1= l.get(0);
my1.setLink(myLinkedClass);
MyClass my2= l.get(1);
my2.setLink(myLinkedClass);
db.save(my1);
db.save(my2);
So what I am asking is, is there any way to save a pojo without also saving the links? Any special method call I can use to say to OrientDb: "Hey don't look into the links when saving this pojo, look only at their RIDs"?

You could save the linked objects first, then the main object. In this way OrientDB will find the object non-dirty and will skip the cascading save.

Related

Find by where NOT current row

I am developing a small cms and I am using spring data jpa to do my database stuff.
When I add a new page, I want to make sure the slug doesn't already exist, for that purpose I added a method to my repository:
public interface PageRepository extends JpaRepository<Page, Integer> {
Page findBySlug(String slug);
}
That works fine when adding.
However when editing a page, I want to check that the slug doesn't already exist but NOT for the current page, how can I do that? I guess I could somehow pass the current row id or something like that, but how would I do that?
You can write
Page findBySlugAndIdNot(String slug,Long id)
where id is name of your identifier in entity with proper type. Look at documentation
you may try custom query like :
#Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM Page p WHERE p.slug = :slug and p.pageId!=pageId")
public boolean existsBySlugInPage(#Param("slug") String slug, #Param("pageId") Integer pageId);

Java EBean Play Framework. findOne() not working ? how to return one object instead of findList()?

//Below code is Not Working
//Here, my query just returns one object, So I am trying to use findOne() //method.
Query<Topic> query = Ebean.find(Topic.class);
Topic topic = new Topic();
Topic topic=Topic.find.where().eq("columnName", "nameToMatch").findOne();
//Below part is working if I use findList(). But I have to do get(0) to //fetch the topic which is not good practice I think.
List<Topic> topicList = Ebean.find(Topic.class).where().eq("columnName", "NametoMatch").findList();
topicList.get(0)
Can anyone provide ideas how to return just One Object instead of list ?
I don't know if findOne exists in Ebean, but when I need to retrieve only one object I use findUnique()
If you're sure the object you want to find is unique, you can get it via findUnique(): Topic.find.where().eq("columnName", "nameToMatch").findUnique();
Otherwise you can use findList() with setMaxRows(), because you don't want to load in memory whole result set:
Topic.find.where().eq("columnName", "nameToMatch").setMaxRows(1).findList();

Using MongoDB 3.4 to load and save userdata

How can I find a document and retrieve it if found, but insert and retrieve it if not found in one command?
I have an outline for the formats I wish my documents to look like for a user's data. Here is what it looks like
{
"username": "HeyAwesomePeople",
"uuid": "0f91ede5-54ed-495c-aa8c-d87bf405d2bb",
"global": {},
"servers": {}
}
When a user first logs in, I want to store the first two values of data (username and uuid) and create those empty values (global and servers. Both those global and servers will later on have more information filled into them, but for now they can be blank). But I also don't want to override any data if it already exists for the user.
I would normally use the insertOne or updateOne calls to the collection and then use the upsert (new UpdateOptions().upsert(true)) option to insert if it isn't found but in this case I also need to retrieve the user's document aswell.
So in a case in which the user isn't found in the database, I need to insert the outlined data into the database and return the document saved. In a case where the user is found in the database, I need to just return the document from the database.
How would I go about doing this? I am using the latest version of Mongo which has deprecated the old BasicDBObject types, so I can't find many places online that use the new 'Document' type. Also, I am using the Async driver for java and would like to keep the calls to the minimum.
How can I find a document and retrieve it if found, but insert and retrieve it if not found in one command?
You can use findOneAndUpdate() method to find and update/upsert.
The MongoDB Java driver exposes the same method name findOneAndUpdate(). For example:
// Example callback method for Async
SingleResultCallback<Document> printDocument = new SingleResultCallback<Document>() {
#Override
public void onResult(final Document document, final Throwable t) {
System.out.println(document.toJson());
}
};
Document userdata = new Document("username","HeyAwesomePeople")
.append("uuid", "0f91ede5")
.append("global", new Document())
.append("servers", new Document());
collection.findOneAndUpdate(userdata,
new Document("$set", userdata),
new FindOneAndUpdateOptions()
.upsert(true)
.returnDocument(ReturnDocument.AFTER),
printDocument);
The query above will try to find a document matching userdata; if found set it to the same value as userdata. If not found, the upsert boolean flag will insert it into the collection. The returnDocument option is to return the document after the action is performed.
The upsert and returnDocument flags are part of FindOneAndUpdateOptions
See also MongoDB Async Java Driver v3.4 for tutorials/examples. The above snippet was tested with current version of MongoDB v3.4.x.

OptimisticLockException with Ebean/Play

I have a Play 2.1.3 Java app using Ebean. I am getting the OptimisticLockException below.
[OptimisticLockException: Data has changed. updated [0] rows sql[update person
set name=? where id=? and email=? and name=? and password is null and created=?
and deleted is null] bind[null]]
I understand that it is trying to tell me the record has changed between when I read it and when I tried to write it. But the only change is happening in this method.
public void updateFromForm(Map<String, String[]> form) throws Exception {
this.name = form.get("name")[0];
String password = form.get("password")[0];
if (password != null && password.length() != 0) {
String hash = Password.getSaltedHash(password);
this.password = hash;
}
this.update();
}
Am I doing this wrong? I saw similar logic in zentasks. Also, should I be able to see the the values for the bind variables?
UPDATE: I am calling updateFromForm() from inside a controller:
#RequiresAuthentication(clientName = "FormClient")
public static Result updateProfile() throws Exception {
final CommonProfile profile = getUserProfile();
String email = getEmail(profile);
Person p = Person.find.where().eq("email", email).findList().get(0);
Map<String, String[]> form = request().body().asFormUrlEncoded();
if (p == null) {
Person.createFromForm(form);
} else {
p.updateFromForm(form);
}
return ok("HI");
}
I have an alternative approach to this, where I add the annotation
#EntityConcurrencyMode(ConcurrencyMode.NONE)
to the Entity class.
This disables the optimistic locking concurrent modification check meaning the SQL becomes
update person set name=? where id=?
This is even more optimistic since it simply overwrites any intermediate changes.
Little bit late, but for your case #Version annotation should be the solution. We're using it mostly with java.util.Date, so it can be also used also for determining the date of last record update, in Play model that's just:
#Version
public java.util.Date version;
In such case update statement will be done with id and version fields only - useful especially when using with large models:
update person set name='Bob'
where id=1 and version='2014-03-03 22:07:35';
Note: you don't need/should update this field manually at each save, Ebean does it itself. version value changes ONLY when there was updated data (so using obj.update() where nothing changes doesn't update version field)
Mystery solved.
First- this public service announcement. "OptimisticLockException" is a big bucket. If you are trying to track one of these down be open to the idea that it could really be anything.
I figured out my problem by dumping SQL to the log and finding this:
update person set name='Bob'
where id=1 and email='jj#test.com'
and name='Robert' and password is null
and created=2013-12-01 and deleted is null
So I guess what happens when you do an update is that it builds a WHERE clause with all the known entities and their values as they were originally ready.
That means, if any other part of your code or another process changes something behind your back, this query will fail. I wrongly assumed that the problem was that somehow .setName('Bob') had changed the name in the DB or some object cache.
Really what was happening is that the WHERE clause includes a date while my database includes an entire timestamp with date, time, and timezone.
For now, I fixed it by just commenting out the timestamp in the model until I can figure out if/how Ebean can handle this data type.
I had the same problem,
after hours of search i found the reason..
It was of inconsistency of the parameters type in the data base (in my case string) and the object i created and tried to save -java.util.Date.
after changing the database to hold datetime object the problem was solved

Ebean lazy load to see if resource exists

I am creating a REST API using play framework. I want to use lazy loading ( finder.ref(id) or Ebean.getReference(id) ) to see if an enity with a specific id exists in database. If it doesn't exist, I will return a 404.
If I try to delete using an id that doesn't exist, an OptimisticLockException is thrown. But that doesn't seem like a valid basis to see if an entity exists.
Is it possible to check if an entity exists by an id using lazy loading? I can always do finder.byId(id) and that can get me what I want. But I want to do this efficiently.
Thanks
You can just count items with specified id, while your id is unique, it will return 1 if item exists and 0 if it doesn't, so you can easily make a condition:
boolean itemExists
= (YourModel.find.where().eq("id", id).findRowCount() == 1) ? true : false;
Logger.info("Item " + ((itemExists) ? "exists" : "not found!"));
On the other hand if your intension is returning existing entity for an example in Json, you don't need to make separate checking, just check if it's not null...
YourModel entity = YourModel.find.byId(id);
if (entity == null) return notFound("No such record");
// .. rest of code for preparing API...
Edit
About costs: find.byId(id) tries to fetch whole entity, while find.ref(id) gets only reference. Unfortunately you can't determine if object exists by ref(id) as it's always not null, therefore IMHO counting elements by id is cheaper than selecting even single field to check if Db returns the entity.
Actually find.byId(id) is most expensive option as it loads whole entity, for well optimized APIs it's usually better to write dedicated methods using Ebean's select() and fetch(), like:
YourModel e = YourModel.find.select("id, name, age").where().eq("id", id).findUnique();
or
List<YourModel> le = YourModel.find.select("id, name, age").where().eq("id", id).findList();

Categories