ORMLite throws 'generated-id key was not set by the update call' - java

I am trying to store a class to MySql database. The class is declared as follows:
#DatabaseTable(tableName = "categories")
public class CategoryItem {
#DatabaseField(generatedId = true)
protected long mUniqueId;
#DatabaseField
private String mCategoryDisplayName;
#DatabaseField
private int mItemsCount; // How many items are from this category
#DatabaseField(foreign = true, foreignAutoRefresh = true, maxForeignAutoRefreshLevel = 5)
private CategoryItem mParent;
protected CategoryItem() {}
When trying to store this class to MySql database (in openshift), I receive the following exception:
java.sql.SQLException: generated-id key was not set by the update call
com.j256.ormlite.stmt.mapped.MappedCreate.insert(MappedCreate.java:115)
com.j256.ormlite.stmt.StatementExecutor.create(StatementExecutor.java:438)
com.j256.ormlite.dao.BaseDaoImpl.create(BaseDaoImpl.java:308)
com.kanooli.common.itemslist.ORMLite.ORMLiteItemsAdapter.addCategory(ORMLiteItemsAdapter.java:51)
com.kanooli.common.itemslist.Update.updateDataBase(Updater.java:85)
kanooliserver.mysql.DbUpdater.doGet(DbUpdater.java:65)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
When I looked in the source code, this is where this print comes:
if (key == null) {
// may never happen but let's be careful out there
throw new SQLException("generated-id key was not set by the update call");
}
Is this a bug in ORMLite?

java.sql.SQLException: generated-id key was not set by the update call
This is strange and should not happen. I don't believe this is a bug in ORMLite. I believe that this indicates that you have a mismatch between the schema that created the database table and the entity that is being inserted into the table. Please verify that the schema that created the MySQL table actually has the id field defined something like:
`id` INTEGER AUTO_INCREMENT
If you created the table without AUTO_INCREMENT and then tried to insert an entity into the table that thought it was generatedId = true then that would result in the exception that you have seen.
I've added some checks for this specific issue, some tests to look for it, and better messaging. See these check-ins:
Better messaging
Additional checks and some tests
Specific MySQL tests

Related

Entity with oracle sequence as ID throws org.hibernate.PropertyAccessException

When I try to save my entity in my oracle db using the .save() method of the hibernate session I get this exception :
org.hibernate.PropertyAccessException: Could not set field value [1757] value by reflection : [class mypackage.MyEntity.myid] setter of mypackage.MyEntity.myid
The value 1757 is correct, it's the one I want for my ID. (when I do a select myschema.mysequence from dual; in my DB I get the next value of this one so it seems to work properly)
I don't understand the error, it's like it doesn't find the setter method but when I explore the exception, in the cause it says something else :
Can not set java.lang.Long field mypackage.MyEntity.myid to null value
which, for me, doesn't make any sense since the value "1757" is not null at all obviously ^^, and Long should accept null values anyway so I have no idea why it doesn't work. I supposed it was because an ID column coulnd't be null (which is false since we also can have a default for null values). So really, I don't get it.
My entity :
#Entity
#Table(name = "MYTABLE")
public class MyEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="seqName", sequenceName="myschema.mysequence",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seqName")
#Column(name = "MY_ID")
private Long myid; //NUMBER
//some other column
public Long getMyid() {
return myid;
}
public void setMyid(Long myid) {
this.myid = myid;
}
//some other accessors
}
I tried to change the type "Long" to long and I tried to do some other setters with different types (long, int, BigInteger, Integer, BigDecimal...) but nothing changed.
I'm able to create an occurrence using the "createNativeQuery" method with "myschema.mysequence.nextval" in myid field like this :
session.createNativeQuery("INSERT INTO myschema.MYTABLE (MY_ID, someotherfields)"
+ " VALUES (myschema.mysequence.nextval, someotherfieldvalues)")
.executeUpdate();
but if I do so, the id is lost... I want to get it back from the query like it's supposed to be with the session.save() method.
For now, the only way I found to get my ID from sequence after insert statement was to create it before (SELECT myschema.mysequence.nextval FROM DUAL) and it's terrible for the performances. We often add rows by thousands.
Try adding a no-parameter constructor to your entity.
Did you try debugging to the point where the exception is initially thrown and inspect what is happening exactly? Maybe some underlying exception is swallowed. Are you sure you updated Hibernate to the latest version 5.4.34/5.5.7? If so, and you still have the problem, please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue.

ORMLite Java, casing of sequences

I am trying to connect to postgresql with ORMlite from a Java client.
the DB gets generated perfectly, but when I try to insert something into a table that is using an autoincrement id I get the following error:
org.postgresql.util.PSQLException: ERROR: relation "commandusage_id_seq" does not exist
When I check the DB I can see a "commandUsage_id_seq" sequence has been created. With a Capital U.
How can I configure ORMLite to use all the same casing for both creating and interacting with the DB ? I couldn't find this in the documentation
Thanks in advace.
update:
When explicitly setting the sequenceId I can circumvent the issue
generatedIdSequence = "commandusage_id_seq"
but still I would like to know if this is possible by setting some config for ORMLite instead of setting this per DBObject class
update2:
URL of the package to prevent confusion: ORMLite
update3:
Below a snippet of the code and how it works. Again I would like to know if ORMLite is capable of doing the to lowercase conversion automatically instead of me doing it explicitly.
#DatabaseTable(tableName = "commandusage", daoClass = CommandUsageDaoImpl.class)
public class CommandUsage {
#DatabaseField(columnName = "id", generatedIdSequence = "commandusage_id_seq")
private transient int identifier;
When I check the DB I can see a "commandUsage_id_seq" sequence has been created. With a Capital U.
Interesting. After some initial confusion on my part, this seems like a bug a in ORMLite. The pattern is if you force a table name with mixed case (typical is to downcase the name) and then ask for a sequence-id on it in Postgresql:
#DatabaseTable(tableName = "TableNameCaseWithSeqeuence")
private static class TableNameCaseWithSeqeuence {
#DatabaseField(generatedId = true)
public int id;
...
}
I've got a fix in trunk but it's going to take a bit to spin a release.
The workaround right now is to extend the PostgresDatabaseType and inject it into your ConnectionSource. It should do something like:
public OurPostgresDatabaseType extends PostgresDatabaseType {
// constructors ...
#Override
public String generateIdSequenceName(String tableName, FieldType idType) {
String name = tableName + DEFAULT_SEQUENCE_SUFFIX;
return downCaseString(name, true);
}
}

Tell Hibernate to only let the Database generate a value when it's null

I'm implementing the PUT Methods for a REST-API.
I have a POJO similar to the following:
public class Brand implements Serializable {
#Column(columnDefinition = "serial")
#Generated(GenerationTime.INSERT)
#JsonIgnore
private Integer id;
#Id
#JsonProperty("brand")
private String brand;
.
.
.
}
Within the postgresql database the brand table has the following columns:
a database-internal id (SERIAL) which shall not be visible to the outside.(This is used manly for joining tables)
a brand (TEXT) which is the primary key
My Service method looks like this:
public Brand updateBrand(String brand, Brand update) {
Brand b = brandRepository.findBrandByBrand(brand);
if(b == null) { //If not exists create new one
b = new Brand(null, brand);
}
else { //If exists keep id, delete old one and create new entry
if(update != null && update.getBrand() != null) {
brandRepository.delete(b);
}
ServiceUtils.copyProperties(update, b); //This is within the if clause, because brand is the only value
}
return brandRepository.save(b);
}
And the controller would have something like this:
#PutMapping(value = "/brand/{brand}")
public ResponseEntity<Brand> updateBrand(#PathVariable("brand") String brand,
#RequestBody Brand update) {
Brand updated = articleNumberService.updateBrand(brand, update);
if(updated == null) {
throw new EntryCreationFailedException(brand); //self made exception
}
return new ResponseEntity<>(updated, HttpStatus.OK);
}
Now my following problem is, that when calling PUT ../brand/stackoverflow
with body:
{"brand":"StackOverflow")
it deletes the old stackoverflow brand (which had id=1, for example) and creates a new one called StackOverflow. But when checking the database the id column is incremented (so now it has id=2).
I checked and this is caused by hibernate still calling:
insert
into
brand
(brand)
values
(?)
This definitly is what I want when id is null. Which happens when creating a new Brand for example. But when only overriding the brand and id is not null I want hibernate to call this:
insert
into
brand
(id, brand)
values
(?, ?)
I know this would be possible by creating a own save method and in an emergency override the query. BUT I'm quite optimistic that this should be possible without. Bt I can't realy find fitting answers to this. I already had problems finding the proper annotations for the postgresql-specific serial behavior.
P.S: I know that some will shout "why would you have Brand as the primary key and not id!?" But this only is a simple class/part of the database. There are more complex classes which use exactly the same way for the internal database id, (and actually need it), but have multiple primary keys etc. So this rather is a very simple representation for my problem.
Since the Postgres use the serial as the auto_increment, it's not included the id insertion in sql statement. Behind the scene, it create the sequence for it. So you should use the Generation type IDENTITY for it.
If it was a field with #Id you would be able to write a custom #GenericGenerator that fetched the sequence if the value was null, but as it's not a primary key, I think you would end up having to have a separate parent Entity with its own generated Id.

JPA Annotation #Column(insertable=false) is being ignored, why?

I want one of the fields to be ignored when called save() method. The field is gonna get populated automatically by the database and returned. It should be treated as a read-only field.
I am concerned about private Timestamp ts; field:
#Entity
#Table(name = "time_series", schema = "ms")
#IdClass(Reading.class)
public class Reading implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "name", nullable = false)
private String sensorName;
#Id
#Column(name = "ts", insertable = false, updatable = false)
private Timestamp ts;
#Column(name = "reading")
private Double value;
...
As you see, I use insertable = false, updatable = false are inside the #Column annotation, so I'd expect that ts is ignored when forming the actual SQL behind the curtain.
#Override
#Transactional(readOnly = false)
public Reading save(Reading r) {
return readingRepository.save(r);
}
ReadingRepository is basically extended Spring's CrudRepository which has save(...) method.
When I save Reading object with ts=null I get an error from Postgres:
ERROR: null value in column "ts" violates not-null constraint
because Spring Data did not actually ignore the ts field based what I see from the log:
insert into ms.time_series (ts, name, reading) values (NULL, 'sensor1', 10.0)
Clearly, I want the query to be without ts like this:
insert into ms.time_series (name, reading) values ('sensor1', 10.0)
Why is the field not being ignored?
Now if you ask me whether my database schema is okay I say yes. When I type SQL query in console without the ts everything is fine. I even tried #Generated and #GeneratedValue annotations. Name and ts are both forming a primary key for the table, however, the result is the same if I make only one of them a PK or if I add an extra surrogate ID column. Same result...
Am I overlooking something or is there maybe a bug in the Spring framework?? I am using Spring 5.1.2 and SpringData 2.1.2
Note: If I use #Transient annotation that persists the insert query correctly but then the field is being ignored completely even on read/fetch.
Many thanks for any help with this!
Try using GenericGenerator and GeneratedValue in your code.
Add the needed annotation and give values to all other members in Reading class, except ts.
Here some examples.
As you say
I get an error from Postgres
If you check the docs it states:
Technically, a primary key constraint is simply a combination of a unique constraint and a not-null constraint.
That's also true for multi-column primary keys (see here)
So, if ts is part of your primary key in the database (as the #Id indicates) it's simply not possible to insert null values in that column.
IMO Hibernate/Spring got nothing to do with that as
insert into ms.time_series (ts, name, reading) values (NULL, 'sensor1', 10.0)
should be equivalent to
insert into ms.time_series (name, reading) values ('sensor1', 10.0)

Null pointer exception while using hibernate

I am using Hibernate and spring. this is my model class
#Entity
#NamedNativeQueries({#NamedNativeQuery(
name = "CSI_TARGET",
query = "select * from CSITARGET('CSIINDEX',2)",
resultClass = CSITarget.class)})
public class CSITarget {
#Column(name="csi_target")
private BigDecimal csi_target;
#Id
#Column(name="financialyearfrom" ,nullable = true)
private int financialyearfrom =0;
#Column( name="at_yearhalf" , nullable = true)
private String at_yearhalf = "";
public BigDecimal getCsi_target() {
return csi_target;
}
public void setCsi_target(BigDecimal csi_target) {
this.csi_target = csi_target;
}
public int getFinancialyearfrom() {
return financialyearfrom;
}
public void setFinancialyearfrom(int financialyearfrom) {
this.financialyearfrom = financialyearfrom;
}
public String getAt_yearhalf() {
return at_yearhalf;
}
public void setAt_yearhalf(String at_yearhalf) {
this.at_yearhalf = at_yearhalf;
}
I am using Hibernate to call a stored procedure in postgres database. The stored procedure returns a table which is mapped to this model class. Now my problem is, the table that is returned from the database contains a null value. I am in the need of doing some manipulations on the data. Now since the null value is mapped to the bean class I am getting a null pointer exception. How can I make hibernate ignore the null values in the database and set a default value for the corresponding property in the bean class. As you can see I have used nullable property also. It does'nt work.
financialyearfrom is int which cannot be assigned null value though corresponding column you might be having null value in database if column is defined as nullable.
For handling null values in java primitive variables, remove nullable=true and possible add default value 0, so all null value from db column would convert to 0 or 0.0 etc.
Or
Use wrapper class instead i.e. Integer which will allow you to retain null value assigned from db column.
Again, above two approaches are in general applicable for primitive variables using in Hibernate entities.
Further to add #ID column shouldn't be nullable IMO, if it corresponds to primary key column (in most of the cases it is) so your code would be wrong as primary key column doesn't allow null values.
Would it be possible to use COALESCE in your query to assign a default value to that field if its null? If that's possible that's probably the best way to fix this issue w/o having to tweak your code too much.

Categories

Resources