Find by where NOT current row - java

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);

Related

OrientDb how to not update links when using the object database

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.

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();

Android db.update not work if use db.delete first

I have a strange problem to update a table in my database...forgive me if I can not explain well but I'm a bit confused...
The problem is this:
I created a table with values, I read this values in my listview..everything works for now..insert and delete values works without problem..now created a loop in a service why do I need to make a comparison between a value and a string of my database and when this comparison is true, I need to change a value in my table..
The real problem is this: my db.update works only if not use ... never, the command db.delete... if use it, the db.update not work anymore ..and to make it work again, i need to make a new AVD.
how is it possible?
my db.delete and id is this:
item.getMenuInfo();
id = getListAdapter().getItemId(info.position);
public void deleteReg(SQLiteDatabase db ,long id)
{
db.delete(TabRegistry.TABLE_NAME, TabRegistry._ID + "=" + id, null);
}
on activity:
databaseHelper.deleteReg(db, id);
my db.update is this: (positions is a value of getPositions(),for locate a positions with a cursor(always works, even when fails db.update))
public void updateReg(SQLiteDatabase db,int positions, String stat)
{
ContentValues v = new ContentValues();
v.put(TabRegistry.STATUS, stat);
db.update(TabRegistry.TABLE_NAME, v, TabRegistry._ID + " = " + positions, null);
}
on service:
databaseHelper.updateReg(db, positions, "SUCCESS");
if you need more code, tell me what I add now..thanks in advance
The SQLite api you are using is based off of CRUD operations (you should read this).
You are DELETE-ing the record from the database, therefore there is nothing to UPDATE when you attempt to update it. If you want to create a new record, or recreate the one you deleted then you would perform an INSERT instead of an UPDATE.
EDIT:
It also appears you are passing in position number to the update and delete. I assume that you are also using this value to place the record in your table? Is it possible that when you delete the record from the table and the database, that the other records now have an invalid position because they haven't been updated also? It's just a shot in the dark, figured I might as well ask.

What is wrong with this GeoTools FeatureId?

Using the GeoTools WFS-T plugin, I have created a new row, and after a commit, I have a FeatureId whos .getId() returns an ugly string that looks something like this:
newmy_database:my_table.9223372036854775807
Aside from the fact that the word "new" at the beginning of "my_database" is a surprise, the number in no way reflects the primary key of the new row (which in this case is "23"). Fair enough, I thought this may be some internal numbering system. However, now I want a foreign key in another table to get the primary key of the new row in this one, and I'm not sure how to get the value from this FID. Some places suggest that you can use an FID in a query like this:
Filter filter = filterFactory.id(Collections.singleton(fid));
Query query = new Query(tableName, filter);
SimpleFeatureCollection features = simpleFeatureSource.getFeatures(query);
But this fails at parsing the FID, at the underscore of all places! That underscore was there when the row was created (I had to pass "my_database:my_table" as the table to add the row to).
I'm sure that either there is something wrong with the id, or I'm using it incorrectly somehow. Can anyone shed any light?
It appears as if a couple things are going wrong - and perhaps a bug report is needed.
The FeatureId with "new" at the beginning is a temporary id; that should be replaced with the real result once commit has been called.
There are a number of way to be aware of this:
1) You can listen for a BatchFeatureEvent; this offers the information on "temp id" -> "wfs id"
2) Internally this information is parsed from the Transaction Result returned from your WFS. The result is saved in the WFSTransactionState for you to access. This was before BatchFeatureEvent was invented.
Transaction transaction = new transaction("insert");
try {
SimpleFeatureStore featureStore =
(SimpleFeatureStore) wfs.getFeatureSource( typeName );
featureStore.setTransaction( transaction );
featureStore.addFeatures( DataUtilities.collection( feature ) );
transaction.commit();
// get the final feature id
WFSTransactionState wfsts = (WFSTransactionState) transaction.getState(wfs);
// In this example there is only one fid. Get it.
String result = wfsts.getFids( typeName )[0];
}
finally {
transaction.close();
}
I have updated the documentation with the above example:
http://docs.geotools.org/latest/userguide/library/data/wfs.html

Categories