ORMLite Java, casing of sequences - java

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

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.

Hibernate annotation for single select object (with where) instead of on-to-many collection

Currently we have a class that looks something like that (depersonalised and non-relevant parts removed):
#Entity
#Table(name = "MAIN_TABLE")
public class MainTable extends AbstractTable {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "mainTable")
#OrderBy("CREATED_ON DESC")
private Set<MainTableState> states;
...
public MainTableState getActiveState(){
if(this.states == null || this.states.isEmpty()){
return null;
}
MainTableState latest = states.iterator().next();
// The reason we use this for-loop, even though we have the #OrderBy annotation,
// Is because we can later add states to this list, which aren't automatically ordered
for(MainTableState state : states){
if(state.getCreatedOn() != null && latest.getCreatedOn() != null &&
state.getCreatedOn().after(latest.getCreatedOn()){
latest = state;
}
}
return latest;
}
...
}
So currently it will retrieve all MainTableStates from the DB by default, and if we need the activeState we use the for-loop method. Obviously this is pretty bad for performance. Currently we don't use this list at all (the purpose was to have a history of states, but this has been postponed to the future), but we do use the getActiveState() method quite a bit, mostly to show a String inside of the MainTableState-class in the UI.
In addition, even if we would always use a TreeSet and keep it sorted so we won't need the loop but only need states.iterator().next() instead, it will still initialize the list of states. With some heavy performance testing we had more than 1 million MainTableState-instances when it crashed with an java.lang.OutOfMemoryError: GC overhead limit exceeded.
So, we want to change it to the following instead:
#Entity
#Table(name = "MAIN_TABLE")
public class MainTable extends AbstractEntity {
#???
private MainTableState activeState;
...
public MainTableStates getActiveState(){
return activeState;
}
...
}
So, my question, what should I put at the #??? to accomplish this? I'm assuming I need the #Formula or something similar, but how can I say to hibernate it should return a MainTableState object? I've seen #Formula being used with MAX for a date, but that was to get that date-property, not get an entire object based on that max date.
After #user2447161's suggestion I've used a #Where-annotation, which does indeed help to reduce the Collection size to 1 (sometimes), but I have two more related questions:
How to use #OnToMany and #Where but get a single object, instead of a list of objects of size one? Is this even possible? Here in a answer from December 2010 it is stated it isn't. Has this been fixed somewhere in the last six years?
How to deal with the random alias in the where clause? I could do something like this:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "mainTable")
#Where(clause = "CREATED_ON = (SELECT MAX(mts.CREATED_ON) FROM MAIN_TABLE_STATES mts WHERE mts.FK_MAIN_ID = ???.MAIN_ID)")
private Set states; // TODO Get single object instead of collection with size 1
The problem with is that ??? is a random alias generated by hibernate (sometimes it's this_, sometimes it's something along the lines of mainTable_1_, etc.). How to set this alias for the entire query to the DB to use it here? I also tried MAIN_TABLE.MAIN_ID instead which doesn't work, and with no alias it also doesn't work because it uses the MainTableState-alias instead of MainTable-alias (like this below).
from
MAIN_TABLE this_
left outer join
MAIN_TABLE_STATUSES mainstat2_
on this_.main_id=mainstat2_.fk_main_id
and (
mainstat2_.created_on = (
SELECT
MAX(mts.created_on)
FROM
MAIN_TABLE_STATUSES mts
WHERE
-- mainstat2_.main_id should be this_.main_id instead here:
mts.fk_main_id = mainstat2_.main_id
)
)
Well, regarding your question #2, as it looks like you need a quick solution with minimal impact in your existing code, this may be acceptable: you can use an Interceptor to deal with the alias and generate the right sql statement. Do this:
use a unique string as alias placeholder in your #Where clause, for instance:
...WHERE mts.FK_MAIN_ID = ${MAIN_TABLE_ALIAS}.MAIN_ID...
if your application doesn't have one yet, create an Interceptor class extending EmptyInterceptor and configure it as a SessionFactory interceptor
override the onPrepareStatement method to replace the placeholder with the alias found after 'from MAIN_TABLE' with something like this:
public String onPrepareStatement(String sql) {
String modifiedSql = sql;
if (sql.contains("${MAIN_TABLE_ALIAS}")) {
String mainTableAlias = findMainTableAlias(sql);
modifiedSql = sql.replace("${MAIN_TABLE_ALIAS}", mainTableAlias);
}
return modifiedSql;
}
Be aware that this method will be called for every sql statement that hibernate generates in your application.
Additionaly, your #Where clause only works properly when a join is used, so you should set the fetch mode explicitly adding
#Fetch(FetchMode.JOIN)
to the states property to avoid that hibernate may use the select mode.

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

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

How to use Postgres inet data type with OpenJPA?

I need to record IP addresses in a Postgres (9.0) table with OpenJPA (2.2.2).
I've got it working using a native query:
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
int rows = entityManager.createNativeQuery("insert into click (ip) values (?::inet)") //
.setParameter(1, InetAddress.getLocalHost().getHostAddress()) //
.executeUpdate();
entityManager.getTransaction().commit();
entityManager.close();
But I'd prefer to figure out a way for OpenJPA to handle it w/o a native query.
I searched and found some other suggestions like annotating the column like this:
#Column(length = -1, columnDefinition = "inet")
But that doesn't seem to do the cast. I get this exception:
ERROR: column "ip" is of type inet but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
Position: 32 {prepstmnt 1376458920 INSERT INTO Click (ip) VALUES (?) [params=?]} [code=0, state=42804]
Can I annotate the field w/ #Strategy and implement a custom FieldStrategy or ValueHandler? This seems like the right solution, but the documentation is pretty light and I can't find a simple example to start from.
https://openjpa.apache.org/builds/2.2.2/apache-openjpa/docs/ref_guide_mapping_custom.html
Any direction on fixing the annotations or implementing a FieldStrategy or ValueHandler would be appreciated.
edit: I figured it out. Since I don't have enough rep, I can't answer my own question.
I created a custom DBDictionary that correctly does the cast.
package package.name.goes.here;
import org.apache.openjpa.jdbc.schema.Column;
public class PostgresDictionary extends org.apache.openjpa.jdbc.sql.PostgresDictionary {
#Override
public String getMarkerForInsertUpdate(Column col, Object val) {
String colType = col.getTypeIdentifier().getName();
if (colType != null) {
return "?::" + colType;
}
return "?";
}
}
Then simply annotate the field with:
#Column(columnDefinition = "inet")
And register the custom dictionary in persistence.xml:
<property name="openjpa.jdbc.DBDictionary" value="package.name.goes.here.PostgresDictionary"/>

Need to know if each field has changed, how should I model this in Hibernate

So I have a class with three fields that maps to a table using hibernate
Class Widget
{
String field1;
String field2;
String field3;
}
On application startup a number of instances these widgets will be added to the database from an external files, but when I exit the application I need to know which (if any) of these fields have been changed by the user since the application was started, so the changes can be saved back to the files. I also need to store the original value for logging purposes.
I can't work whether I need a status field in the table or whether there is already a way of doing this using Hibernate/Database.
EDIT:A good solution to the program was given below . however the main reason I am using Hibernate is to reduce memory consumption so storing the original values when changed is not a good solution for me , I want everthing stored in the database. So I have create this new question How do I store a copy of each entity I add to database in Hibernate
Given an entity like the following you can track changes on one of it's field (while preserving its original value too).
#Entity
#Table(schema = "test", name = "test")
public final class Test {
private static final int ORIGINAL = 0;
private static final int CURRENT = 1;
private Integer id;
// holds the original and current state of the field
private final AtomicReferenceArray<String> field = new AtomicReferenceArray<>(2);
#Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Transient
public String getOriginalField() {
return field.get(ORIGINAL);
}
#Basic
public String getField() {
return field.get(CURRENT);
}
public void setField(String field) {
this.field.compareAndSet(ORIGINAL, null, field);
this.field.set(CURRENT, field);
}
#PreUpdate
public void preUpdate() {
System.out.format("Original: %s, New: %s\n", getOriginalField(), getField());
}
...
}
If there is a single row in a database like this:
id: 1
field: a
version: 2011-12-02 11:24:00
before the field gets updated (say, from a to b) you'll get the following output.
Original: d, New: b
The original value gets preserved even if the the entity is updated multiple times and both state can be accessed through the corresponding getters (getField and getOriginalField—you can get more creative than me in the naming :).
This way, you can spare yourself from creating version columns in your database and also can hide the implementation details from clients.
Instead of an AtomicReferenceArray you could use arrays, lists, etc, to track all changes like this way.
The #PreUpdate isn't necessary of course, but this way you can be notified of changes in the entity's state and atomically save the updated fields into file. There more annotations like these: see the documentation for javax.persistence for other annotation types.
If you are using MySql then you can get table's last update time from information_schema database like
SELECT UPDATE_TIME FROM `information_schema`.`tables`
WHERE TABLE_SCHEMA = 'dbName' AND TABLE_NAME = 'tableName'
Or else simple solution will be to add a column for update time stamp. By this you can even monitor which particular row has been updated.
If you need to synchronize with files as soon as you save into database, You can use the Hibernate event mechanism to intercept any save to database and save it to file, here's a sample doing that.

Categories

Resources