Issue in Adding data into database using hibernate - java

I am getting the following exception while adding data into database:
org.hibernate.HibernateException: The database returned no natively generated identity value
I am using the following code:
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(user);
logger.info("Successfully data insert in database");
tx.commit();
isSaved = true;
Please let me know what is wrong. Thankx

It seems as if the database doesn't support the identity id generator. Based on your mapping you are probably using the userName as the ID column, which would mean that you probably want to set the generator class to assigned since the username (= id) will be picked manually (and not auto generated by the database):
<hibernate-mapping>
<class name="com.test.User" table="user">
<id name="userName" column="user_name">
<generator class="assigned" />
</id>
<property name="userCode">
<column name="user_code" />
</property>
</class>
</hibernate-mapping>

Related

Primary key not updating when inserting data to database using hibernate

I'm new to hibernate and postgres. I need help regarding primary key in hibernate. This is the problem: When I inserted data using persist() for the first time, it was a success. Now, when I inserted the next data, my IDE gave me this error:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique
constraint "test1_tbl2_pkey"
Detail: Key (id)=(0) already exists.
It seems like the value in the sequence is not used. Because the initial value of a sequence in postgres is 1. Also, when I looked at the tables, their primary key values are 0. How to tell hibernate to use the sequence in my postgres database? I'm new to both technologies thus, I'm having a hard time solving this problem.
tbl1_mappings.hbm.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="pojo.Test1Tbl1" table="test1_tbl1" schema="public">
<id name="id" type="integer" column="id">
<generator class="increment"/>
</id>
<property name = "name" column = "tbl1_name" type = "string"/>
<one-to-one name="tbl2"></one-to-one>
</class>
</hibernate-mapping>
tbl2_mappings.hbm.xml
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="pojo.Test1Tbl2" table="test1_tbl2" schema="public">
<id name = "id" type = "integer" column = "id">
<generator class="foreign"/>
</id>
<property name = "name" column = "tbl2_name" type = "string"/>
</class>
</hibernate-mapping>
Event code of my button
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
Test1Tbl2 tbl2 = new Test1Tbl2(field2.getText());
Test1Tbl1 tbl1 = new Test1Tbl1(field1.getText(), tbl2);
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
session.persist(tbl2);
session.persist(tbl1);
tx.commit();
}
catch(HibernateException ex) {
ex.printStackTrace();
}
catch(Exception ex) {
ex.printStackTrace();
}
}
After I've done my intensive research, I found a solution. Although, this solution doesn't use the persist() method. However, this solution does update the primary key via postgres sequence. Btw, I'm using Hibernate 6.1.2.Final version.
This is the code that I came up with:
Transaction tx = null;
try (Session session = factory.openSession()){
tx = session.beginTransaction();
String query = "INSERT INTO test1_tbl1(tbl1_name)" +
"VALUES ('"+field1.getText()+"');";
MutationQuery mq = session.createNativeMutationQuery(query);
int result = mq.executeUpdate();
System.out.println("Rows affected: " + result);
tx.commit();
}
catch(Exception ex) {
if (tx!=null) tx.rollback();
ex.printStackTrace();
}
Take note that I severed the relationship between test1_tbl1 and test1_tbl2 tables and I only used test1_tbl1 to make my answer more clear. This article helps me to create the solution above:
MutationQuery and SelectionQuery
Other articles propose to use the 'sequence' generator class in the mapping file to fix the problem. Ex:
<id name="id" type="integer" column="id">
<generator class="sequence">
<param name="sequence_name">my_sequence</param>
</generator>
</id>
Although, this solution doesn't work for me. I also tried using different types of generator classes like native, identity and auto; still they didn't work.
These are some articles that I've stumbled upon that are related to the problem mentioned in this thread:
Hibernate doesn't use PostgreSQL sequence to generate primary key
JPA and PostgreSQL with GenerationType.IDENTITY
How To Use Sequence In Hibernate As A Property In XML Mapping
HIbernate 5: generator class="sequence" not working
Migrating Hibernate 3 to 5: relation hibernate_sequence does not exist
JPA GenerationType.AUTO not considering column with auto increment
Edit:
I decided to play around and I found the solution that I'm looking for. In your cfg.xml, remove hibernate.transform_hbm_xml.enabled property. This property is messing up the generator classes. However, removing the property will make this warning appear in your logs:
WARN: HHH90000028: Support for `<hibernate-mappings/>` is deprecated [RESOURCE : resources/mappings/tbl1_mappings.hbm.xml]; migrate to orm.xml or mapping.xml, or enable `hibernate.transform_hbm_xml.enabled` for on the fly transformation
Switching to JPA annotation style with persistence.xml fixes this warning for me.
Btw, this is my cfg.xml:
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.PostgreSQLDialect
</property>
<property name = "hibernate.connection.driver_class">
org.postgresql.Driver
</property>
<property name = "hibernate.connection.url">
jdbc:postgresql://localhost:3308/hibernate_test
</property>
<property name="hibernate.default_schema">
public
</property>
<property name = "hibernate.connection.username">
postgres
</property>
<property name = "hibernate.connection.password">
password
</property>
<property name = "hibernate.id.new_generator_mappings">
true
</property>
<!-- List of XML mapping files -->
<mapping resource="resources/mappings/tbl1_mappings.hbm.xml"/>
</session-factory>
</hibernate-configuration>
The hibernate.id.new_generator_mappings property is a flag that sets the generator mapping style to old style if it's set to false. Otherwise, sets the mapping style to new style.
I think this article has simple explanation about the property:
HIbernate 5: generator class="sequence" not working

Hibernate (hbm) - refreshing set with where clause after merging the container object

I'm working with Hibernate 4.3.5, Java 1.6 and Spring 4.0.3.
I've mapped the entities through hbm, and I want my application works with logical deletion.
So, in each mapped entity, I've added a property named 'deleted', which indicates if an entity is deleted or not.
Because I don't want to load the deleted entities (the ones having true the deleted property), I've used the where clause in the mapped classes, so I only get the entities aren't logically deleted.
And also, I've added the same where clause to every one-to-many relationship.
In one particular case, I've got a Report entity that has a one-to-many relationship with the Document entity.
So, when I mark a Document as deleted, and I save the Report entity (with merge), I expect than the Report entity doesn't keep the Document marked as deleted. But this doesn't occur.
This is the hbm for the Report entity:
<hibernate-mapping>
<class
name="es.entities.Report"
table="reports"
dynamic-insert="false"
dynamic-update="false"
where="deleted = 0">
<id name="id">
<generator class="identity"/>
</id>
<property name="title"></property>
<property name="deleted"></property>
<set
name="documents"
table="documents"
cascade="all"
lazy="false"
where="deleted=0">
<key column="id_report"/>
<one-to-many class="es.entities.Document"/>
</set>
</class>
</hibernate-mapping>
Here it is the hbm for the Document entity:
<hibernate-mapping>
<class
name="es.entities.Document"
table="documents"
dynamic-insert="false"
dynamic-update="false"
where="deleted = 0">
<id name="id">
<generator class="identity"/>
</id>
<property name="name"></property>
<property name="type"></property>
<property name="size"></property>
<property name="deleted"></property>
</class>
</hibernate-mapping>
I use a Service (ReportService) to open a Spring transaction. The method is:
#Autowired
private ReportDao reportDao;
#Transactional
public Report save(Report report) {
this.reportDao.save(report);
}
And this is the DAO (ReportDao) method I use to save the Report entity:
public Report save(Report report) {
return (Report) this.currentSession().merge(report);
}
I put an example:
The parameter I send to the service contains a Report object, with two Document objects, one of them deleted and the other not.
The DAO method returns the same information, but I'd like this method returns only the documents are not deleted.
Note: if I use another method with another transaction, I obtain the report only with the document is not deleted, but I'd like to do this in the same transaction.
Can anybody help me or show me an alternate to this? It is possible to use other Session method than merge?
Thanks a lot.
Merge method create a copy from the passed entity object and return it. Try re-fetching the report entity post merge.

How to change the ordering of sql execution in hibernate

I am trying to model bidirectional parent-child design, with ordered children.
When removing child (eg. child #2 of 3 children) from the parent, hibernate's generated sql led to unique constraint violation because "update" (sibling) is being executed before the "delete" (target).
The RDBMS that I use (H2) does not support deferred constraint. What are the options I have besides the following ?
remove the unique constraint from schema
explicitly manage the sorting myself, rather than depend on hibernate
Is there any way to make hibernate generate sql with 'delete' precedes 'update' ?
Some old discussion found in the forum:
DELETE then INSERT in collection - Order of executed SQL
DB Schema:
CREATE TABLE IF NOT EXISTS Sequences (
ID BIGINT NOT NULL AUTO_INCREMENT,
Name LONGVARCHAR,
Type LONGVARCHAR,
Sequence LONGVARCHAR,
ParentId BIGINT DEFAULT NULL,
Index INT,
CONSTRAINT pk_SequenceId PRIMARY KEY (ID),
CONSTRAINT uc_Sequences UNIQUE (ParentId, Index),
CONSTRAINT fk_Sequences
FOREIGN KEY (ParentId)
REFERENCES Sequences(ID)
ON UPDATE CASCADE
ON DELETE CASCADE
);
Class:
public class Sequence {
protected Long ID;
protected String name;
protected String type;
protected String sequence;
protected Sequence parentSequence;
protected List<Sequence> childSequences = new ArrayList<Sequence>();
}
HBM Mapping:
<hibernate-mapping>
<class name="Sequence" table="Sequences">
<id name="ID" column="ID" type="long">
<generator class="native"/>
</id>
<property name="name" type="string" column="Name"/>
<property name="type" type="string" column="Type"/>
<property name="sequence" type="string" column="Sequence"/>
<many-to-one name="parentSequence" column="parentId" cascade="save-update" insert="false" update="false" class="Sequence" />
<list name="childSequences" inverse="false" lazy="true" cascade="all-delete-orphan">
<key column="parentId" not-null="true"/>
<list-index column="Index" base="0"/>
<one-to-many class="Sequence"/>
</list>
</class>
Hibernate does not execute the HQL (or SQL) statements directly but in the moment of a commit() or flush() it re-orders the SQL statement with the goal to do them in the most effective way. But it can happen the re-order from Hibernate is wrong and for example causes constraint violations, as in your case.
The solution is to introduce an intermediate flush(). flush() forces the re-order and send the SQL statements, but it does not commit.
In your case you can modify your code like (as a sketch):
transaction = session.beginTransaction();
session.delete(obj);
session.flush(); /* newly introduced */
session.update(...);
transaction.commit();
If the problem should be in a cascade delete or some delete executed by Hibernate without your control, then you have to take the control over the delete and update process and do the operations explicitely in your code instead of relying on the automatisms of Hibernate.

LONG as primary key in Hibernate mapping to MySQL

I am trying to implement persistence of some Java objects via Hibernate mapping to a MySQL table. When I commit I get a message saying 'Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1'.
My hypothesis is that the problem is caused from having a long-field in my Java POJO that I want to use as my primary key in the MySQL table. Since I was not able to use datatype LONG as my primary key in MySQL table (ERROR 1170: BLOB/TEXT column 'id' used in key specification without a key length) I concluded from some googling and this post that BIGINT would be the suitable mapping for long. However it is not updating.
My test POJO Personis very simple. It has 3 fields: id (long), firstname (String), lastname (String) with setters and getters, etc.
I do the hibernate mapping in xml (person.hbm.xml) that essentially looks like (minus headings):
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="native"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
My actual java code snippet that is supposed to save or update the record is simple:
Transaction tr = session.beginTransaction();
Person person = new Person(1,"John","Doe");
session.saveOrUpdate(person);
tr.commit();
And here's that thing, this all works just fine if I change the type of id to an int (Integer) in the Person object and in the MySQL table. However, I do not have that option for the actual objects that I want to persist so the question is; what am I doing wrong or what should I do to get it to work? Thanks.
ADDING Stacktrace:
Hibernate: update hibernatetest set firstname=?, lastname=? where id=?
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:57)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3006)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2908)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3237)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:187)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1082)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:317)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at com.hibernate.test.TestMain.main(TestMain.java:38)
nested transactions not supported
UPDATE:
OK, I have finally worked it out. I changed the hibernate generator class from 'native' to 'assigned' and now it works as expected. So now the hibernate mapping looks like:
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="assigned"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
Must admit I did not know the meaning of that parameter (copied from somewhere) and had no idea it could cause this much headache. Found this explanation which was quite useful.
Apparently I do not have enough credentials to answer my own questions so I guess that it will remain open or if someone provides an empty answer, I will accept it. Thanks.
When you use the saveOrUpdate() method hibernate fires the insert query if the id of the object is null and update if it is any other value. I can see the code,
Person person = new Person(1,"John","Doe"); setting the id to 1 and calling the saveOrUpdate() method. I am assuming there are no entries for the id 1 and hence the error is thrown.
To make it work, you need to make the below changes.
Change the Type of id in person to Long from long(The wrapper class so that it can support null).
Write the constructor new Person("John","Doe"); and save that object.
It is not a good Idea to keep the <generator class="assigned"/> for the transactional data. Instead you should be sticking to the native as you were trying first.
I feel this is a cleaner way to solve your initial problem, even though you have found an alternate solution.

Hibernate not loading associated object

i am trying to load a hibernate object ForumMessage but in it contain another object Users and the Users object is not being loaded.
My ForumMessage Mapping File:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Jan 4, 2011 10:10:29 AM by Hibernate Tools 3.4.0.Beta1 -->
<hibernate-mapping>
<class name="com.BiddingSystem.Models.ForumMessage" table="FORUMMESSAGE">
<id name="ForumMessageId" type="long">
<column name="FORUMMESSAGEID" />
<generator class="native" />
</id>
<property name="ForumMessage" type="java.lang.String">
<column name="FORUMMESSAGE" />
</property>
<many-to-one name="User" class="com.BiddingSystem.Models.Users" fetch="join">
<column name="UserId" />
</many-to-one>
<property name="DatePosted" type="java.util.Date">
<column name="DATEPOSTED" />
</property>
<many-to-one name="Topic" class="com.BiddingSystem.Models.ForumTopic" fetch="join">
<column name="TopicId" />
</many-to-one>
</class>
</hibernate-mapping>
and I am using the follwing code:
Session session = gileadHibernateUtil.getSessionFactory().openSession();
SQL="from ForumMessage";
System.out.println(SQL);
Query query=session.createQuery(SQL);
System.out.println(query.list().size());
return new LinkedList <ForumMessage>(query.list());
<many-to-one name="User" class="com.BiddingSystem.Models.Users" fetch="join" lazy="false">
You need to add lazy="false" as well.
You can add lazy="false" to the many-to-one mapping which will load the users when the ForumMessage is loaded. Alternatively you could initialize the users list using Hibernate.initialize(). Just make sure you do this before you close the session.
Session session = gileadHibernateUtil.getSessionFactory().openSession();
string sql = "from ForumMessage";
Query query = session.createQuery(sql);
List results = query.list()
for(ForumMessage message : results)
{
Hibernate.initialize(message.User);
}
return new LinkedList <ForumMessage>(results);
You should only do one of these though if you have a need to. Hibernate by default lazy loads objects to avoid unnecessary calls to the database. For example:
public LinkedList getMessages()
{
//It's assumed the session is opened and closed elsewhere.
string sql = "from ForumMessage";
Query query = session.createQuery(sql);
List results = query.list();
//The overhead of extra calls to the database occur here.
//This would have a similar impact if lazy load is set to false.
for(ForumMessage message : results)
{
Hibernate.initialize(message.User);
}
return new LinkedList <ForumMessage>(results);
}
public void printMessages()
{
LinkedList messages = getMessages();
for(ForumMessage message : messages)
{
System.out.println(message.ForumMessage);
}
}
In the above code sample the overhead is incurred for loading all the Users objects but those objects are never used. If Hibernate's lazy-loading were used then this extra overhead would not be incurred. In the following example the list of users isn't loaded until the list is used. This way calls are not made to the database until the data is actually needed.
public LinkedList getMessages()
{
//It's assumed the session is opened and closed elsewhere.
string sql = "from ForumMessage";
Query query = session.createQuery(sql);
List results = query.list();
return new LinkedList <ForumMessage>(results);
}
public void printMessages()
{
LinkedList messages = getMessages();
for(ForumMessage message : messages)
{
//Hibernate will load the users objects here when they are accessed.
for(Users user : message.User)
{
System.out.println(user);
}
}
}
One point to be careful of when lazy loading is all loading must be done in an active session. If you don't have an active session and you try and access something that has not yet been loaded Hibernate will throw a LazyInitializationException.
In addition, using Hibernate's lazy load functionality complies more with the idea of persistence ignorance where as using Hibernate.initialize() does not.

Categories

Resources