I'm trying to test my hibernate mapping based on a set of given JUnit tests. The following test however fails.
#Test
public void testEntity3Constraint() {
expectedException.expect(PersistenceException.class);
expectedException.expectCause(isA(ConstraintViolationException.class));
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// Entity #3
IUplink ent3_1 = daoFactory.getUplinkDAO().findById(testData.entity3_1Id);
ent3_1.setName(TestData.N_ENT3_2);
em.persist(ent3_1);
em.flush();
} finally {
tx.rollback();
}
}
This is the exception I'm getting:
Expected: (an instance of javax.persistence.PersistenceException and exception with cause is an
instance of org.hibernate.exception.ConstraintViolationException)
but: exception with cause is an instance of org.hibernate.exception.ConstraintViolationException cause
<org.hibernate.PersistentObjectException: detached entity passed to persist: dst.ass1.jpa.model.impl.UplinkImpl>
is a org.hibernate.PersistentObjectException
As you can see The Constraint Violations and Persistence Exceptions are swapped in terms of instance and cause. The exception is thrown when I'm calling entitymanager.persist. How can I obtain the expected exception?
I cannot change the expected exception and nor do I want to. I need to find a way to get the exception expected by the test.
Thanks in advance!
You are expecting javax.persistence.PersistenceException
but get a similar yet different org.hibernate.PersistentObjectException.
Just change the Exception you expect in your test.
Related
Trying to query data from legacy database (with different datasource) to current database, but got two exceptions in two scenarios.
I've got an implementation like this:
class A {
#PersistenceContext(unitName = "EPU")
EntityManager schemaEntityManager;
#PersistenceContext(unitName = "ELegacyPU")
EntityManager oldSchemaEntityManager;
void init() {
queryFromDatabase();
// check if it is empty, if true then query from legacy
queryFromLegacyDatabase();
recreateLegacyInCurrentDatabase();
}
List<E> queryFromDatabase() {
// query from schemaEntityManager
}
List<ELegacy> queryFromOldDatabase() {
// query from oldSchemaEntityManager
}
void recreateLegacyInCurrentDatabase() {
// records -> schemaEntityManager.merge()
}
}
While I'm trying to run this with #Translational over recreateLegacyInCurrentDatabase method, then got exception from this method (cannot found any valid info over hibernate's TRACE log-level):
javax.persistence.TransactionRequiredException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)
at org.jboss.as.jpa#24.0.0.Final//org.jboss.as.jpa.container.AbstractEntityManager.transactionIsRequired(AbstractEntityManager.java:880)
at org.jboss.as.jpa#24.0.0.Final//org.jboss.as.jpa.container.AbstractEntityManager.merge(AbstractEntityManager.java:567)
While I'm trying to run this with #Translational over only class or over only queryFromDatabase/queryFromOldDatabase method or over any of this two cases + recreateLegacyInCurrentDatabase, then got exception from queryFromOldDatabase (cannot found any valid info over hibernate's TRACE log-level):
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not prepare statement
at org.hibernate#5.3.20.Final//org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at org.hibernate#5.3.20.Final//org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1575)
at org.hibernate#5.3.20.Final//org.hibernate.query.Query.getResultList(Query.java:132)
at org.hibernate#5.3.20.Final//org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter.getResultList(CriteriaQueryTypeQueryAdapter.java:74)
...
Caused by: org.hibernate.exception.GenericJDBCException: could not prepare statement
...
Caused by: java.sql.SQLException: IJ031070: Transaction cannot proceed: STATUS_MARKED_ROLLBACK
I use JTA configured under persistence.xml & trying not to use RESOURCE_LOCAL instead of this or there is no option to do this without RESOURCE_LOCAL configuration in persistence.xml?
My code looks something like this:
#Transactional
public void save(Citizen citizen){
this.saveCitizen(citizen);
}
private void saveCitizen(Citizen citizen){
try{
citizenReposiory.save(citizen);
} catch(DataIntegrityViolationException exception){
//Exception on the line below
Citizen existingCitizen = citizenReposiory.findById(citizen.getId());
exisitingCitizen.setAge(50);
}
}
I'm first trying to save the citizen. If the exception is thrown it's because the citizen already exists in the database. In this case I want to update the existing row instead. However, in the code above I will get another exception when calling citizenReposiory.findById(citizen.getId());. Here's a snippet of the terminal:
[26-04-2020 00:35] WARN [o.h.engine.jdbc.spi.SqlExceptionHelper] - SQL Error: 1062, SQLState: 23000
[26-04-2020 00:35] ERROR [o.h.engine.jdbc.spi.SqlExceptionHelper] - Duplicate entry '10-2020-1' for key 'UKe4wgjj1wdqag5qhbcgnxhbvuj'
[26-04-2020 00:35] ERROR [org.hibernate.AssertionFailure] - HHH000099: an assertion failure occurred
(this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session):
org.hibernate.AssertionFailure: null id in dk.rsyd.mature.entities.WeeklyCare entry (don't flush the
Session after an exception occurs)
org.hibernate.AssertionFailure: null id in dk.rsyd.mature.entities.WeeklyCare entry (don't flush the
Session after an exception occurs)
What is happening here? Is it not possible to continue with an transaction after catching an exception? I have tried to add #Transactional(noRollbackFor = DataIntegrityViolationException.class) but that didn't help.
A different approach could be used. That is, you could first perform the findByID, and verify that the findByID returns a value, if it returns a value, and therefore it already exists, you can carry out the setAge operation, otherwise you can save the citizen. In this way you will always do a preliminary check and avoid saving an object that does not exist by going in exception.
If "The Citizen Object" that you submit to citizenReposiory.save() already have the primary key inside. Maybe you can just call saveOrUpdate() simply.
private void saveCitizen(Citizen citizen){
citizenReposiory.saveOrUpdate(citizen);
}
FYI
Hibernate saveOrUpdate behavior
Hibernate save() and saveOrUpdate() methods
I've been asked to write some coded tests for a hibernate-based data access object.
I figure that I'd start with a trivial test: when I save a model, it should be in the collection returned by dao.getTheList(). The problem is, no matter what, when I call dao.getTheList(), it is always an empty collection.
The application code is already working in production, so let's assume that the problem is just with my test code.
#Test
#Transactional("myTransactionManager")
public void trivialTest() throws Exception {
...
// create the model to insert
...
session.save(model);
session.flush();
final Collection<Model> actual = dao.getTheList();
assertEquals(1, actual.size());
}
The test output is expected:<1> but was:<0>
So far, I've tried explicitly committing after the insert, and disabling the cache, but that hasn't worked.
I'm not looking to become a master of Hibernate, and I haven't been given enough time to read the entire documentation. Without really knowing where to start, this seemed like this might be a good question for the community.
What can I do to make sure that my Hibernate insert is flushed/committed/de-cached/or whatever it is, before the verification step of the test executes?
[edit] Some additional info on what I've tried. I tried manually committing the transaction between the insert and the call to dao.getTheList(), but I just get the error Could not roll back Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started
#Test
#Transactional("myTransactionManager")
public void trivialTest() throws Exception {
...
// create the model to insert
...
final Transaction firstTransaction = session.beginTransaction();
session.save(model);
session.flush();
firstTransaction.commit();
final Transaction secondTransaction = session.beginTransaction();
final Collection<SystemConfiguration> actual = dao.getTheList();
secondTransaction.commit();
assertEquals(1, actual.size());
}
I've also tried breaking taking the #Transactional annotation off the test thread and annotating each of 2 helper methods, one for each Hibernate job. For that, though I get the error: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here.
[/edit]
I think the underlying DBMS might hide the change to other transactions as long as the changing transaction is not completed yet. Is getTheList running in an extra transaction? Are you using oracle or postgres?
How to handle exceptions raised by #Transactional annotation. In a case where TransactionSystemException includes a ConstraintViolationException due to not null constraint violation for an entity annotated with #Entity.
I am using Hibernate.
This particular exception should be handled by fixing the bug it reveals: the code is trying to create an object with a null property, and this property may not be null. It means that the code forgot to populate this property, or didn't handle the validation of the user-entered data correctly.
I have left thinking about "having a need of exception finally!" here is another way to test and validate the constraints and constraint violations. Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set> constraintViolations = validator.validateValue(Course.class, "name", null); assertEquals(1, constraintViolations.size()); –
#Transactional
public void start() {
...
...
int result = entityManager
.createQuery("update Users set name=" + value + " where user.id=5").executeUpdate();
.....
}
Above code gives javax.persistence.TransactionRequiredException exception.
Update database in the middle of the transaction, possible ?
Any suggestions ?
Thanks.
A.
I just wonder if
This is a runtime exception which is thrown by the persistence provider when a transaction is required but is not active. A transaction is required because the start method is annotated as transactional. To get rid of the exception, you'll have to investigate why the line is called out of a transaction context.
A database update may be possible during a (different) transaction. Depends on the tables that are locked by the active transaction and on the transaction strategy. But in this case, it looks like you need to activate a transaction before you enter the start method.
With JPA you'd do something like this:
em = emf.createEntityManager();
tx = em.getTransaction();
tx.begin(); // now a transaction is active
start(); // call your method
// call other methods...
tx.commit(); // now the update is actually done
em.close();
Note - this is close to pseudo code, exception handling is missing in this example.