Hibernate , Upon Transaction commit failure , find which entity failed - java

i have a simple loop which will insert some entities in the same transaction , into a DB2 database. The problem is that when committing the transaction and an insert fails due to duplicate ID , the exception i get from the driver is a plain message that indicates for which table the insert statement failed , but i need to find for which entity. Is there any way to translate the exception thrown from the Database??
the method is :
public final void saveAll(List<Entity> entities)
{
StatelessSession session = getSaveQuerySession();
Transaction tr = session.beginTransaction();
try
{
for(Entity entity : entities)
session.insert(entity);
tr.commit();
}
catch (Exception e)
{
e.printStackTrace();
tr.rollback();
}
finally{
session.close();
}
}
the exception i got is :
org.hibernate.exception.ConstraintViolationException: could not insert: [com.MyEntity]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2438)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2858)
EDIT
the inner exception is :
java.sql.SQLIntegrityConstraintViolationException: [SQL0803] Duplicate key value specified.
Cause . . . . . : A unique index or unique constraint *N in *N exists over one or more columns of table MYENTITY in MYSCHEMA.
The operation cannot be performed because one or more values would have produced a duplicate key in the unique index or constraint. Recovery . . . :
Change the statement so that duplicate keys are not produced.
For information on what rows contain the duplicate key values,
look at the previously listed messages in the job log (DSPJOBLOG command) or press F10 (Display messages in job log) on this display.

Related

How to continue processing after a constraint violation exception?

We have a Kafka consumer in a Java - Spring Boot service that has multiple instances in multiple clusters (all with same consumer group id). The consumer saves data to the database (SQL Server).
The code checks if a record exists in the ItemSet table before saving to the database. The actual data in the payload gets saved in a child table ItemValue and not the parent, ItemSet. The relational data table hierarchy is one to many in this order ItemSet -> ItemName -> ItemValue. ItemSet table has a unique constraint for department id, season combination to prevent multiple duplicate adds.
I need to do some processing after catching this exception to ensure that the incoming data still gets saved under the existing ItemSet and doesn't get lost. I am using Spring Data JPA and as soon as I catch the exception and try to retrieve the existing record I end up getting:
org.hibernate.AssertionFailure: null id in ItemSet entry (don't flush the Session after an exception occurs).
The getItemSet() in the catch block blows up.
What is the best way to overcome these race conditions?
ItemSet savedItemSet = null;
try
{
String seasonName = itemSet.getSeasonName();
Long seasonYear = itemSet.getSeasonYear();
Long departmentId = itemSet.getDepartment().getId();
List<ItemSet> itemSets = attributeSetRepository.findBySeasonNameAndSeasonYearAndDepartmentId(
seasonName, seasonYear, departmentId);
LOGGER.info("Found {} item sets corresponding to season name : {}, season year : {}, "
+ "department id : {}", itemSets.size(), seasonName, seasonYear, departmentId);
if(CollectionUtils.isEmpty(itemSets)) {
savedItemSet = itemSetRepository.save(itemSet);
}
else {
return new CreatedItemSet(itemSets.get(0).getId());
}
}
catch(PersistenceException | DataIntegrityViolationException e)
{
LOGGER.error("An exception occurred while saving itemSet set", e);
if (e.getCause() instanceof ConstraintViolationException)
{
String seasonName = itemSet.getSeasonName();
Long seasonYear = itemSet.getSeasonYear();
Long deptId = itemSet.getDepartment().getId();
LOGGER.info("A duplicate item set found in the database corresponding "
+ "to season name : {}, season year : {} and department : {}",
seasonName, seasonYear, deptId);
ExistingItemSet existingItemSet = getItemSet(seasonName,
seasonYear, deptId);
if(existingItemSet == null) {
LOGGER.info("No item set found");
return null;
}
return new CreatedItemSet(existingItemSet.getId());
}
}
You can't "continue". A transaction is marked for rollback and the persistence context is unusable after a constraint violation happens.
You can either try to avoid the constraint violation, by checking if the DB contains an entry before persisting/updating, or you run the rest of your code in a separate transaction if a constraint violation happens.

JPA EntityManger Transction Commit with error

I have a table A_TABLE, through entityManger going to commit record using begin transction and try to commit. but in table we have combination of some value is there then throwing ORA-00001: unique constraint exception. And that transction is not active can't able to rollback.
So I deleted that record in DB, try to save same value record with new entityManger transction begin, but its try to saving the record two times, because old one got ORA-00001: unique constraint exception entity also going in this transction to DB and in console two insert is calling.
Need solution for this,
Code :
#Autowired
private EntityManager entityManger;
TableEntity entityObject = new TableEntity(); // Always creating new objects
entityObject .setYear(2018); // Combination of year and month
//if its availbe in DB its throw ORA-00001: unique constraint exception
entityObject .setMonth(01);
if (!entityManager.getTransaction().isActive()) {
entityManager.getTransaction().begin();
}
entityManager.persist(entityObject);
try{
if (entityManager.getTransaction().isActive()) {
entityManager.getTransaction().commit();
}
} catch (Exception e) {
if (entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback(); // Not come to this part when ORA-00001: unique constraint exception occur
}
throw e;
}
INSERT call twice above code after delete unique record from DB, but still throwing ORA-00001: unique constraint exception occur because old one got ORA-00001: unique constraint exception entity also going in this transction to DB and in console two insert is calling.

Java Exception doesn't have the same exception message returned from db2 console?

I want to insert multiple rows in a table from a console/tool (e.g.: Data studio) I get the following error message
THE INSERT OR UPDATE VALUE OF FOREIGN KEY FK$MAR$S IS INVALID.
SQLCODE=-530, SQLSTATE=23503, DRIVER=4.13.111
This means I have some trouble with a FOREIGN KEY variable, but I solved that later and it works well.
My problem is that when I'm running the same query from a Java application using PreparedStatement.executeBatch() (batch because it could insert more than one row at a time), I get a different error message:
com.ibm.db2.jcc.am.wn: [jcc][t4][102][10040][3.57.82] Batch failure.
The batch was submitted, but at least one exception occurred on an
individual member of the batch. Use getNextException() to retrieve
the exceptions for specific batched elements. ERRORCODE=-4228,
SQLSTATE=null
When I used getNextException(), I get the following:
com.ibm.db2.jcc.am.co: A NON-ATOMIC INSERT STATEMENT ATTEMPTED TO
PROCESS MULTIPLE ROWS OF DATA, BUT ERRORS OCCURRED
And the error code is -4228.
Why this difference? I want the java application return the same error details as the console tool, so I can handle those exceptions in my java code.
For example, if the returned error code=-803 which means duplicate exception, I would handle my code to make update instead of insert, or if the returned message contains some words like " FOREIGN KEY ", I'll tell user to make sure about lookup tables and so on
I use DB2 version 10.5.3 on z/OS and the DB2 driver version is : 3.65.92
} catch (SQLException ex) {
while (ex != null) {
if (ex instanceof com.ibm.db2.jcc.DB2Diagnosable) {
com.ibm.db2.jcc.DB2Diagnosable db2ex = (com.ibm.db2.jcc.DB2Diagnosable) ex;
com.ibm.db2.jcc.DB2Sqlca sqlca = db2ex.getSqlca();
if (sqlca != null) {
System.out.println("SQLCODE: " + sqlca.getSqlCode());
System.out.println("MESSAGE: " + sqlca.getMessage());
} else {
System.out.println("Error code: " + ex.getErrorCode());
System.out.println("Error msg : " + ex.getMessage());
}
} else {
System.out.println("Error code (non-db2): " + ex.getErrorCode());
System.out.println("Error msg (non-db2): " + ex.getMessage());
}
ex = ex.getNextException();
}
...
}
Above is an example of handling db2 exceptions. The example of output when there are 2 violations simultaneously: unique key on the table MYSCHEMA.MYTABLE where batch inserts come, and a foreign key on a parent table. I split it intentionally into 2 parts:
Before getNextException():
Error code: -4229
Error msg : [jcc][t4][102][10040][4.19.66] ... getNextException().
ERRORCODE=-4229, SQLSTATE=null
After getNextException():
SQLCODE: -803
MESSAGE: One or more values in the INSERT statement,
UPDATE statement, or foreign key update caused by a DELETE statement
are not valid because the primary key, unique constraint or unique
index identified by "1" constrains table "MYSCHEMA.MYTABLE" from
having duplicate values for the index key.. SQLCODE=-803,
SQLSTATE=23505, DRIVER=4.19.66
SQLCODE: -530
MESSAGE: The insert or update value of the FOREIGN KEY
"MYSCHEMA.MYTABLE.MYTABLE_FK" is not equal to any value of the parent
key of the parent table.. SQLCODE=-530, SQLSTATE=23503, DRIVER=4.19.66
I think the batch exception message is pretty clear. Consider that different statements in a batch might fail or issue warnings for different reasons. The batch level error message is therefore generic and instructs you to use "getNextException() to retrieve the exceptions for specific" statements in the batch.
Though this is an old thread. I will share the code which worked for me
try{
preparedStatement.batchUpdate( new ClassName{
//code with setting values and batch size});
}catch (Exception e) {
if (e.getCause() instanceof BatchUpdateException) {
BatchUpdateException be = (BatchUpdateException) e.getCause();
SQLException current = be.getNextException();
do {
current.printStackTrace();
} while ((current = current.getNextException()) != null);
}
}
In here I'm trying to get the exception based on BatchUpdateException instance.

org.hibernate.StaleStateException: Batch update returned unexpected row count

I have a schema in that i have 3 tables.Suppose A,B,C.
A is the parent table of child.and B,C are child table.
When I call hibernateSession.saveOrUpdate It is Updating all three tables.
But in child table I don't have data for parent table but in parent table I have data.So it give Exception
org.hibernate.StaleStateException: Batch update returned
unexpected row count from update [0]; actual row count: 0; expected: 1.
So I want to know Is there any Way in hibernate so that if child table has no data then it inserted else updated while updating parent table data.
EDIT 1 :
The query printed like this
Update A set name =? where aid = ?
Update B set adder =? where bid = ?
Update C set city =? where cid = ?
But I don't have record in B and C table.
EDIT:2
So if table B and C has no data then insert data.
Here is my code.
A a = session.get(aid);
B b = a.getb();
C c = a.getc();
// Save B if not exists in database
if (b != null) {
if (b.bid() != null) {
if (session.get(b.bid()) == null) {
System.out.println("--record is there in db---");
session.save(b);
}
}
}
// Save C if not exists in database
if (c != null) {
if (c.cid() != null) {
if (session.get(c.cid()) == null) {
System.out
.println("--record is there in db in update---");
session.save(c);
}
}
}
}
But I am getting this exception.
Hibernate Error: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
You can set show_sql = true in your hibernate configuration.
Then you can see the update statement which throws exception.
It is not enough to say somethin without seeing your data types,
tables and relations.
I recommend to turn on hibernate statement logging in your Hibernate.cfg.xml configuration to know what is happening as you don't post the table structures and the query.
To enable Hibernate statement logging:
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="use_sql_comments">true</property>
Then please update your question with the query printed.
Edit 1:
This exception raise when updating an object that have an id which not exist in the table. The error means it was unable to found record with your given id.
To avoid this:
You should read the record with the same id:
If it founds a record back then call update
Otherwise throw it and show a error like
Exception record not found.
Edit 2
Concerning this error:
Hibernate Error: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
You should tell the hibernate session to merge your modified object with the one in the session
session.merge(object)

MySQL Transactional delete and Java

I've got tables like this:
Table A:
`id | name`
Table B:
`id | A_id | ....`
A_id is a foreign key to Table A, the Engine is InnoDB
This is the code that fails:
String[] cleanupQueries = new String[] { "DELETE FROM B WHERE A_id = (SELECT id FROM A WHERE name = 'test')",
"DELETE FROM A WHERE name = 'test'" };
Connection connection;
try {
connection = DriverManager.getConnection(getConnectionString());
connection.setAutoCommit(false);
} catch (SQLException e) {
throw new RuntimeException("Error establishing a database connection!");
}
try {
for(String cleanupQuery : cleanupQueries) {
PreparedStatement statement = connection.prepareStatement(cleanupQuery);
statement.executeUpdate(); //FAILS WHEN EXECUTING THE SECOND QUERY
}
} catch(SQLException e) {
throw new RuntimeException("Error while executing the queries in the transactional context!");
}
try {
connection.commit();
} catch (SQLException e) {
rollback(connection);
throw new RuntimeException("Error while comitting!");
}
The Exception i get is:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails ('DATABASE/TABLE', CONSTRAINT 'FK_B_A' FOREIGN KEY ('FK_A') REFERENCES 'A' ('ID') ON DEL)
The database doesn't let me delete A while there are still B's left, but the first query deleted all B's. I want to delete all B's and the A they reference only completely.
I don't want to change the Tables to have cascading deletes. What shall i do to get the code working?
Cause for error is
The Foreign Key has referenced to the table A id so if you would like to delete the F_Key , first you should delete the Child references values of that foreign keys then only its possible to delete the parent.
Correct me if 'm wrong..
Simply add the cascade is true while deleting the foreign key constraint.The child table entry is automatically deleted when you delete the original parent entry.
Try:
"DELETE FROM B WHERE A_id = (SELECT id FROM A WHERE name IN 'test')"
Since the child rows are deleted in the same transaction, the deleted rows are still visible and thus the parent rows could not be deleted.
This may be because of the transaction isolation setting on the connection. I would try different levels and see which one allows it.

Categories