I'm writing updates as part of CRUD testing and when I test my code, I get an error saying no entities are found. I have no idea why, because my partner did the exact same code and he worked perfectly. Neither of us is able to figure out what's going on. I'm getting an error on the getSingleResult() method.
#Test
public void updateBookTest() {
Book book = em.createQuery("select b from Book b where b.title = :title", Book.class).setParameter("title", "createABook").getSingleResult();
tx.begin();
book.setTitle("updatedThisBook");
book.setAuthor("newAuthor");
tx.commit();
Book updatedBook = em.find(Book.class, book.getBookId());
assertEquals(book.getTitle(), updatedBook.getTitle());
assertEquals(book.getAuthor(), updatedBook.getAuthor());
System.out.println("updateBookTest:\t" + book.toString());
tx.begin();
book.setTitle("createABook");
tx.commit();
}
This is my code. Let me know if more information is needed.
getSingleResult must to throw a NoResultException if there is no result.
So, your test is Ok.
Check that both are using same database (and there is no data returned), both are running same query, and both are using same jpa implementation versions.
From javadoc (since jpa 1.0):
getSingleResult
java.lang.Object getSingleResult(): Execute a SELECT query that returns a single untyped result.
Returns: the result
Throws:
NoResultException - if there is no result
NonUniqueResultException - if more than one result
IllegalStateException - if called for a Java Persistence query language UPDATE or DELETE statement
QueryTimeoutException - if the query execution exceeds the query timeout value set and only the statement is rolled back
TransactionRequiredException - if a lock mode has been set and there is no transaction
PessimisticLockException - if pessimistic locking fails and the transaction is rolled back
LockTimeoutException - if pessimistic locking fails and only the statement is rolled back
PersistenceException - if the query execution exceeds the query timeout value set and the transaction is rolled back
Reference javadoc getSingleResult
Another point, check that your friend is not calling getResultList instead of getSingleResult. This method returns a list and no throw an exception if empty.
Reference javadoc getResultList
Related
I am using JPA 2.1, Oracle DB and have a list of ids for entities to be removed (about 430000 ids). At first, it was implemented as splitting that id list into each smaller one with 1000 ids, pass them as parameters for a JPQL and executing.
delete from SOPFilter f where f.id in (?1)
Then, I want to change to use JPA CriteriaDelete.
CriteriaDelete<SOPFilter> criteriaDelete = cb.createCriteriaDelete(SOPFilter.class);
Root<SOPFilter> from = criteriaDelete.from(SOPFilter.class);
criteriaDelete.where(from.get(SOPFilter_.id).in(sopFilterIds));
It runs fine until it reach the 90000th one and there is a runtime exception cause it to stop here
org.hibernate.SessionException: Session is closed
and make entity manager factory to close.
INFO : bernate.impl.StmpContainerEntityManagerFactoryBean: Closing JPA EntityManagerFactory for persistence unit 'IMOFFERINGMANAGEMENT'
For whom was mislead by my first post with this exception
java.lang.IllegalStateException: EntityManagerFactory is closed
There was a catch clause to handle runtime exception by adding a record to database before throwing it. And to add a event record, it attempts to create another entity manger from the factory which is closed now.
public static void logEvent(EntityManager em) {
EntityManager em2 = null;
EntityManagerFactory emFactory = em.getEntityManagerFactory();
em2 = emFactory.createEntityManager();
// ...
}
Could anyone shed some light on it?
Im not clear on your code but you are likely hitting a transaction timeout. You can set a hint -
query.setHint("javax.persistence.query.timeout", 8000);
There may also be timeouts on the database side
I want to do a couple of db-operations in a single transaction.
It should be rolled back on all failures but one: in the middel of all the db-ops there will be an insert statement which inserts a generated number into a field that has an unique constraint.
If this insert fails because the generated number is already present, in should be repeated with a newly generated number until it succeeds. But this insert should not affect the other operations.
Since there are no nested tx in JPA, i am not sure what the best practice would be here. i am wondering if this is such an uncommon case.
I tried it with an "embedded" tx, which is opened with requires-new that suspends the surrounding tx and resumes it after successfully inserting the random number.
That worked partially: if there is an exception in the surrounding tx, everything is rolled back but the random number insert.
If i use one single tx, I don't know how i should determine why this tx failed to commit. so i don't know if i have to repeat all the operations with a new random number or leave it because of another failure.
I also tried to explicitely flush the entity manager session after the insert to force the exception before i continue with the rest of the work in hope i could just delete that entity from the EM programmatically and try again.
But I didn't find a way to remove that entity from the EM after a failed insert?
I am also using Spring and SpringDataJPA.
#Transactional
public void doSomeThing() {
SomeEntity fooEntity = new SomeEntity()
someEntityRepository.save(fooEntity);
someMoreDbOPs();
RandomNoEntity rndEnt = new RandomNoEntity();
while (true) {
try {
rndEnt.setRandomNumber(generateRandomNumber());
entityManager.persist(rndEnt);
entityManager.flush();
break;
} catch (ConstraintViolationException)
{
//entityManager.detach(rndEnt); // didnt work
//entityManager.remove(rndEnt); // didnt work
}
}
SomeEntity barEntity = new SomeEntity()
someEntityRepository.save(barEntity);
someMoreDbOPs();
}
i hope this pseudo code helps to describe what i am trying to do.
thanks in advance for any help...
I get this error when I try to run this code.
Error:
javax.persistence.TransactionRequiredException: executeUpdate is not supported for a Query object obtained through non-transactional access of a container-managed transactional EntityManager
Code: (_ut is a UserTransaction object)
public void setMainCategory(Integer deptId, Integer catId) {
try {
Query setmain = _entityManager.createNamedQuery("Category.setAsMain");
Query removeMain = _entityManager.createNamedQuery("Category.removeMain");
setmain.setParameter("categoryId", catId);
Department d;
d=_entityManager.find(Department.class, deptId);
removeMain.setParameter("department", d);
_ut.begin();
removeMain.executeUpdate();
_ut.commit();
_ut.begin();
setmain.executeUpdate();
_ut.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
I have other functions that are identical in implementation and they do not throw this error.
Any suggestions would be greatly appreciated.
Thanks.
You are using an EntityManager to get the Named Queries and also using an (what I think is) injected UserTransaction.
See that the error message says "...Query object obtained through non-transactional access...". This means you are getting the "NamedQuery" through non-transactional access, because the EntityManager is not in the same transaction as _ut. So, you first join the EntityManager to the UserTransaction then you get and execute the query.
Finally your block should look like:
#PersistenceContext(unitName = "myPU")
EntityManager em;
#Inject
UserTransaction ut;
public void doSomeStuff()
{
ut.begin();
em.joinTransaction();
em.createNamedQuery("query1").executeUpdate();
ut.commit();
}
The problem does not come from your method implementation but from your execution context. All method that update database must be executed within an opened transaction. The way you ensure that depends on the way you manage transaction. If transactions are user-managed you have to explicitly retrieve and join an existing transaction or open a new one. If it's container-managed just add #transactional annotation on your method or ensure that there is a method in your call hierarchy holding the annotation.
Here you're using user-managed transactions in a container-managed transaction context (see "container-managed transactional EntityManager" in your error message). You shouldn't so begin and commit / rollback yourself the transactions. If you want to do so, just retrieve an application-managed EntityManager to be able to properly access to JTA transactions.
cf. http://docs.oracle.com/cd/E19226-01/820-7627/bnbqy/index.html
According to the error message, I think the reason is that your order of transaction, you didn't get the Department d in transaction, so the statement of d is detached, then you want to directly update it which will change the statement to managed, the JPA can't do this. just move the find code in the transaction, I think it will be ok.
We use EclipseLink for persistence, and have configured EclipseLink to automatically create the database tables etc., by setting the property eclipselink.ddl-generation to drop-and-create-tables.
This works fine, however EclipseLink (and thus our app) will merrily continue during unit tests, and on actual web app startup even if some of the DDL statements failed.
I noticed this when I incorrectly used the #Index annotation, and wondered why the index was not created, until I noticed in the logs:
org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLException:
Column "MY_INDEX_FLD" not found; SQL statement:
CREATE INDEX X_INDEX ON X (MY_INDEX_FLD)
I really want to know if this happens. Is there some way to tell EclipseLink to make it a fatal error if some DDL statements fails?
I'd like to at least have our (JUnit) integration tests fail in this case.
Bonus points for some way to be able to ignore if the error is simply that the tables are already there (in the case of testing against an existing database).
Apparently, EclipseLink cannot do this.
I had a look at the method in EclipseLink that creates tables:
org.eclipse.persistence.tools.schemaframework.TableCreator.createTables()
(search for "createTables" on the page).
I contains the lines:
try {
schemaManager.createObject(table);
session.getSessionLog().log(SessionLog.FINEST,
"default_tables_created", table.getFullName());
} catch (DatabaseException ex) {
session.getSessionLog().log(SessionLog.FINEST,
"default_tables_already_existed", table.getFullName());
if (!shouldIgnoreDatabaseException()) {
throw ex;
}
}
So apparently EclipseLink assumes that errors during table (and index) creation are always caused by the table already existing :-(.
I filed an EclipseLink bug for this: Bug 356068.
Can somebody tell me the intrinsic reasons why in the JPA 1.0 EntityManager when retrieving an Object via find, you have to deal with null if not found, but when using the Query interface via createQuery getResultList throws a NoResultException when not found.
Maybe i am missing something but I feel its very inconsistent for a Language, and actually I had to do a lot of redesing because of changing from a simple finder to a more fine grained query using the query interface.
Thanks guys.
Queries can be used to retrieve almost anything including the value of a single column in a single row.
If getSingleResult() would return null, you could not tell whether the query did not match any row or whether the query matched a row but the selected column contains null as its value.
When you do a find, jpa will use the primary key to locate the entity object, often using second level cache and it is typically much faster than createQuery and getSingleResult.
You either get null or the Object back from find. When you do a createQuery and instance of Query object is created. If you do a getResultList it will not throw a NoResultException, only if you do a getSingleResult will it throw that exception. If you do a getResultList and none is found, then null will be returned.
Also, NoResultException will mark the transaction rolledback in weblogic 10.3.2.
See this article: NoResultException marks transaction rollback
I think it eliminates this null check :
Object o = q.getSingleResult();
if (o != null)
return (MyObj) o;
return o;
By introducing a RuntimeException (NoResultException) , programmers can safely cast q.getSingleResult() to MyObj , and leave the exception to the caller.
As to q.getResultList() , it will always return a list , null-check is not necessary.
But I still feel this non-intuitive.