handle update hibernate in multithreaded program - java

I run the below code in multithreaded program and get exception:-
org.hibernate.exception.ConstraintViolationException: ERROR: duplicate
key value violates unique constraint "value_pkey"
#Stateless
#TransactionAttribute(javax.ejb.TransactionAttributeType.REQUIRED)
public class GetHelloBean{
#PersistenceContext(unitName = "test-unit")
private EntityManager entityManager;
public Hello<?> insertOrUpdateHello(Hello<?> value) {
Hello<?> existing = null;
try {
existing = this.entityManager.find(Hello.class,
value.getKey());
if (existing != null) {
value = this.entityManager.merge(value);
} else {
this.entityManager.persist(value);
}
this.entityManager.flush();
} catch (Exception e) {
this.logger.error(" value not saved : " + value.toString()
+ " of class " + value.getClass() + ":" + e.getMessage());
}
//
return value;
}
}
Can someone explain why and how can i handle this?

This error may be caused by a race condition in your code. If two or more threads are trying to update a Hello entity with the same key, they may both have a null returned from find and will attempt to persist the entity. Consequently only the "fastest" of the threads will succeed, while others encounter the constraint violation.
If value.getKey() is the primary key of the Hello entities, doing only a merge(...) should be sufficient. Hibernate will check if the entity already exists in the db or the cache and executes an INSERT or UPDATE depending on that. Concurrent executions may still yield unexpected results though (perceived out of order updates).
If it is not the primary key of the Hello entities maybe this answer can help you.

Related

How to handle EntityNotFoundException during Hibernate query?

I'm working on an application where we need to query for a collection of entities using hibernate and then perform some indexing on the results. Unfortunately, the database the application is querying from does not enforce foreign key constraints on all associations, so the application needs to handle potentially broken references.
Currently, the code responsible for doing the query looks like this:
private List<? extends Entity> findAll(Class entity, Integer startIndex, Integer pageSize)
throws EntityIndexingServiceException {
try {
Query query = entityManager
.createQuery("select entity from " + entity.getName() + " entity order by entity.id desc");
if (startIndex != null && pageSize != null) {
return query.setFirstResult(startIndex).setMaxResults(pageSize).getResultList();
} else {
return query.getResultList();
}
}
catch (Throwable e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
log.warn(sw.toString());
return Collections.EMPTY_LIST;
}
}
The problem with this code is that bad data in any of the results will result in the whole page of data being skipped. So if I'm using this method to get a list of objects to index, none of the query results will be included in the indexing even though only one or two were invalid.
I know Hibernate has a #NotFound annotation, but that comes with it's own side-effects like forced eager loading that I would rather avoid. If possible, I want to exclude invalid entities from the results, not load them in a broken state.
So the question then is how can I handle this query in such a way that invalid results (those that cause an EntiyNotFoundException to be thrown) are excluded from the return values without also throwing out the 'good' data?

Reloading entities as though they just got output from a query

I would like to refresh managed entities, I used Session.refresh but it's been causing StackOverflowError because I mapped bi-directionnal relationships.
Plus, I don't want one-to-many relationships to be reloaded nor to keep the same state, I want them to be non-initialized as though their parents entities were a query result.
I tried this :
#Override
public void refresh(IdentifiableByIdImpl entity) {
Query query;
Object refreshedEntity;
try {
query = session.createQuery(
"FROM " + entity.getClass().getSimpleName() +
"WHERE id = " + entity.getId()
);
refreshedEntity = query.uniqueResult();
copyProperties(refreshedEntity, entity);
} catch(StackOverflowError e) {
System.err.println("S.O");
}
}
But it keeps triggering a StackOverflowError.
A simple way would be to return the "refreshedEntity" nonetheless I find this way non-flexible.

Spring3/Hibernate AssertionFailure

Here is a simple hibernate code that inserts a value into a table.
If the row already exists, query the row and return the data.
Most of the time, the code works fine with no issues.
In a very special case, three different clients are trying to insert the exact the same row into the table. Ofcourse, only one row gets inserted. The other two insertions fail and the fall into the try-catch block.
There is a query in the try catch block, which queries the data and sends the value to the client. This results in an error for subsequent operations on the session.
Hibernate throws "ERROR org.hibernate.AssertionFailure - an assertion
failure occured (this may indicate a bug in Hibernate, but is more
likely due to unsafe use of the session)" in the logs.
Here is the code. What would be the right way to handle this scenario?
#Override
public void addPackage(PackageEntity pkg) {
try{
getCurrentSession().save(pkg);
getCurrentSession().flush();
}catch( ConstraintViolationException cve ){
// UNIQ constraint is violated
// query now, instead of insert
System.out.println("Querying again because of UNIQ constraint : "+ pkg);
PackageEntity p1 = getPackage(pkg.getName(), pkg.getVersion());
if( p1 == null ){
// something seriously wrong
throw new RuntimeException("Unable to query or insert " + pkg);
}else{
pkg.setId(p1.getId());
}
}catch (Exception e) {
e.printStackTrace();
}catch (Throwable t) {
t.printStackTrace();
}
}
Primary (or) composite Key makes each row data unique and avoids this error.
If you need the data from all these three requests then create a unique primary key in your table and add it to the entity.
Primary Key could be any unique thing from your data, an auto generated sequence or UUID/GUID.

JPA How to get the value from database after persist

How to get the value which being saved to database after
entityManager.persist
I am able to get the primary key value from database after calling persist not any other values.
E.g.
public void create(Project project) {
entityManager.persist(project);
System.out.println("Id -- " + project.getProjectId());
System.out.println("no -- " + project.getProjectNo());
}
From the above code I am able to get the newly inserted value from project.getProjectId, however not able to get project.getProjectNo
The reason why I am able to get projectId is because it is primary key?
How can I get the value of getProjectNo after persist?
Try refreshing the entity with the database to get the inserted trigger value.
public void create(Project project) {
entityManager.persist(project);
entityManager.getTransaction().commit();
project = entityManager.find(Project.class, project.getProjectId());
entityManager.refresh(project);
System.out.println("Id -- " + project.getProjectId());
System.out.println("no -- " + project.getProjectNo());
}
Documentation

Is the following Hibernate custom ID generator code correct?

I just created a custom hibernate ID generator, and since I'm not an hibernate expert I would like to get some feedback on my code. The generated ID is select max(id) from table, +1.
public class MaxIdGenerator implements IdentifierGenerator, Configurable {
private Type identifierType;
private String tableName;
private String columnName;
#Override
public void configure(Type type, Properties params, Dialect dialect) {
identifierType = type;
tableName = (String) params.getProperty("target_table");
columnName = (String) params.getProperty("target_column");
}
#Override
public synchronized Serializable generate(SessionImplementor session,
Object object) {
return generateHolder(session).makeValue();
}
protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
Connection connection = session.connection();
try {
IntegralDataTypeHolder value = IdentifierGeneratorHelper
.getIntegralDataTypeHolder(identifierType
.getReturnedClass());
String sql = "select max(" + columnName + ") from " + tableName;
PreparedStatement qps = connection.prepareStatement(sql);
try {
ResultSet rs = qps.executeQuery();
if (rs.next())
value.initialize(rs, 1);
else
value.initialize(1);
rs.close();
} finally {
qps.close();
}
return value.copy().increment();
} catch (SQLException e) {
throw new IdentifierGenerationException(
"Can't select max id value", e);
}
}
}
I'd like to know:
How can I make this multi-transaction-safe? (ie if two concurrent transactions insert data, how can I safely assume that I will not end-up having twice the same ID?) -- I guess the only solution here would be to prevent two concurrent hibernate transaction to run at the same time if they use the same generator, is this possible?
If the code could be improved: I feel wrong having to use hard-coded "select", "target_column", etc...
To guarantee point 1), I can fallback if necessary on synchronizing inserts on my java client code.
Please do not comment on the reasons why I'm using this kind of generator: legacy code still insert data onto the same database and uses this mechanism... and can't be modified. And yes, I know, it sucks.
I think the easiest way to achive a transaction-safe behaviour is to put the code you use to retrive the maximum id and do the insert statement, in a transactional block.
Something like:
Transaction transaction = session.beginTransaction();
//some code...
transaction.commit();
session.close()
I also recommend to use HQL (Hibernate Query Language) to create the query, instead of native sql where possible. Moreover, from your description, I have understood that you expect from the query a unique result, the maximum id. So, you could use uniqueResult() method over your query instead of executeQuery.
you can use AtomicInteger for generating ID. That can be used by many threads concurrently.
If you are free to use any other provider of ID then i will suggest to use UUID class for generating random ID.
UUID.randomUUID();
You can refer to link which contain some other ways to generate ID.

Categories

Resources