I try to persist an object in postgres database.
After the persist call, I return my object, I modify my object, research the object (with function find of my entity manager) and data is still the same (before my object's modification).
I believe after persist, the object is link to database, and a modification with a setter is automatically persist in database
I use JPA2.1 with hibernate, wildfly 13.
I already check theses answer :
What is the difference between persist() and merge() in JPA and Hibernate?
JPA EntityManager: Why use persist() over merge()?
I have a repository class with an entity manager :
#Transactional(TxType.SUPPORTS)
class testRepository{
#PersistenceContext(unitName = "xxxPU")
private EntityManager em;
[...]
#Transactional(TxType.REQUIRED)
public Test create(#NotNull Test test) {
em.persist(test);
return test;
}
public Test find(#NotNull Long id) {
return this.em.find(Test.class, id);
}
[...]
}
And my test class with the folowing test :
Test test = new Test(null, null, "12", "RUE DE LA PAIX", "75000", "PARIS", null);
test= testRepository.create(test);
I believe at this point object test is link to database, but if i call this code :
test.setAValue("93000");
And I search this object :
Test testFind = testRepository.find(test.getId());
And compare the two object, I have a test error, because test.aValue = 75000 and testFind.aValue=93000.
After setter, if I made a call to a merge function, testFind.aValue has the correct value.
I don't understand why my object is not link to my database after the persist call.
Related
I am using Spring data jpa and jdbc (using entityManager.unwrap(Session.class) ) connection.
My request flows through 3 method. 1st -> 2nd ->3rd.
1st and 3rd are annotated with #Transactional.
In 1st and 2nd method saving entity through JPA CRUD repository.
In 3rd method using basic jdbc query to retrieve values saved in 2nd method.
As we know in #Transaction entities are not committed to database until JPA commit the transaction.
I used saveAndFlush also in 2nd method but can not see retrieve updated values in method 3 using jdbc query.
1st Method - update()
#Transactional
#Override
public ApiResponse update(RequestInput input) {
ApiResponse response = doRestOfWork(input); // calling 2nd method
// .... some other entity save....
}
2nd Method - doRestOfWork() : setting status=true and calling saveAndFlush method
#Override
public ApiResponse doRestOfWork(Request request) {
Insight insight = insightRepository.findOne(request.getTypeId());
insight.setStatus(true);
insightRepository.saveAndFlush(insight);
processOperation(); // calling 3rd method
}
3rd method - processOperation() : retrieving updated status value through jdbc connection.
#Transactional
public void processOperation() {
Connection conn = null;
SessionImplementor sessionImpl = (SessionImplementor) entityManager.unwrap(Session.class);
try {
conn = sessionImpl.getJdbcConnectionAccess().obtainConnection();
Connection conn = null;
String stmt = "SELECT status from insight";
PreparedStatement ps = conn.prepareStatement(stmt);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
boolean value = rs.getBoolean(status); // returning false, i want this to return true as already set true in 2nd method and called saveAndFlush
}
} catch (SQLException ex) {
} finally {
JdbcUtility.closeResources(conn, ps, rs);
}
}
InsightRepository is extending JpaRepository
#Repository
public interface InsightRepository extends JpaRepository<Insight, Long> {}
I want updated value of status (which is boolean true - updated in method 2) in method 3.
How to achieve this ?
Update
I searched a lot and do not think that if a method is annotated with #Transactional then you can commit changes before completing JPA transaction. So the solution is to remove #Transactional annotation and use entityManager.getTransaction() if want to control over JPA transaction.
Create a method (4th method) for update with #Transactional(propagation = Propagation.REQUIRES_NEW) annotation in a bean different from the 2nd Method and change second method as calling 4th method like this :
#Component //or Service etc..
public class AnotherBean{
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void doUpdateWork(Request request){
Insight insight = insightRepository.findOne(request.getTypeId());
insight.setStatus(true);
insightRepository.save(insight);
}
}
and
#Override
public ApiResponse doRestOfWork(Request request) {
anotherBean.doUpdateWork(request);
processOperation(); // calling 3rd method
}
If you want to controll transaction, dont use #Transactional
em; //entityManager
Transaction tx=em.getTransaction();
try{
tx.begin();
//do work
tx.commit(); // comminted transaction
// start another transaction if you need to, doo more work etc.
...
}
This is most basic and fundamental block of code for using JPA for atomic operations.
Also there is no need to unwrap entity manager, use pure JPA. By unwrapping you are binding current implementation to JPA provider which is in contrary to JPA idea of independency from underlying JPA implementation provider. In other words, it will stop working if for some reasons Spring would change its JPA implementation from Hibrnate to eg. EclipseLink.
I need help with my project since I am not familiar with the spring #transactional annotation. The question is why my application hungs up using the #transactional method but will not hung up without it. Also how do I solve this so that the transaction will suceed.
The scenario is that my application is set-up like this:
Uses declarative transaction Management
One method has the #transactional(rollbackFor=Exception.class) annotation and accesses the database multiple times.
The said method calls another method that returns a String and accesses the database multiple times.
Transaction does not suceed finishing causes a deadlock on my application. It does not return any exception.
The Code below is sample snippet
#Autowired
JdbcTemplate template;
#Transactional(rollbackFor = Exception.class)
public final void upsertData(){
insertTable1(); // insert query to insert in table 1
addReferencingData(); // was just called to update table but returns something which is not used;
//Hangs up before getting to next statement
somePreparedStatmentSQLmergeTable1(mergesql,template); // query to merge in table 1
}
public final String addReferencingData(){
updateTableA(); // update query to update values in table A
updateTableB(); // update query to update values in table B
mergeTable1(); // merge query to update or insert in table 1
return someString;
}
public static void somePreparedStatmentSQLmergeTable1(sql,template){
template.batchUpdate(sql, new BatchPreparedStatementSetter() {
public void setValues(final PreparedStatement ps, final int i){
// setting parameters to be used
}
public int getBatchSize(){
// returns size of data's
}
}
}
Also added the default Transaction manager on my application-context.xml file.Transaction already works based on logs. Only in this specific method it does not work.
Updated some information to be clearer.
I get the an exception when trying to get data, lazily(Exception at the very end)
//application gets data by the following DAO.
public T findById(PK id) {
T result = getHibernateTemplate().get(this.type, id);
getHibernateTemplate().flush();
return result;
}
//Junit test calls a serviceX.getById
#Transactional
public SomeObject getById(int x){
return (SomeObject) aboveDao.findById(x);
}
//Withing the JUnit
SomeObject someObj = serviceX.getById(3);
someObj.getAnotherObject().y.equals("3"); //**Exception** at this line.
//SomeObject class has the following property.
#OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
private AnotherObject anotherObject;
I get the following exception when tryin to access anotherObject in the junit
Methods already tried + extra configuration
We use spring annotation TransactionManager.
<tx:annotation-driven /> specified in the config file.
Also, I tried to add #Transaction(propagation = Propagation.REQUIRED) on top of the JUnit, this did not solve the issue. If I run the application, it works without any issues.
How to solve this type of issue for JUnit?
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role xxxxx , no session or session was closed
Here's what happens
SomeObject someObj = serviceX.getById(3); // #Transactional boundary, no more session
someObj.getAnotherObject().y.equals("3"); // session doesn't exist, can't fetch LAZY loaded object
Because your AnotherObject is LAZY fetched, it doesn't actually get loaded in the getById() method. The Session it was associated with is lost when the #Transactional ends, ie. when execution returns from getById(). Because there is no longer a Session, you get the exception.
You can change your FetchType to EAGER. If you're going to that field of your object, you need to initialize it in your Transaction boundaries.
If you only some times need the anotherObject, a possible solution is to create a #Transactional method that calls the getById and eagerly loads the object.
#Transactional
public SomeObject eagerGetById(int x){
SomeObject obj = getById(x);
obj.getAnotherObject(); // will force loading the object
return obj;
}
Calls this method whenever you need to eagerly load the object.
This is could be useful to you LazyInitilializationException
I'm trying to use hibernate to build up a local cache of data that I pull from source websites. I have the the objects configured with JPA (if it makes a difference) and can read/write them fine in a test application.
Now I want to move the code into a generic "Caching" class so that I can request an object from the Cache and process it as normal. I can read the object from the database and pass it back to the calling class, but when I try and access the collections in the object I get the dreaded lazy initialization exception.
I know what causes this, the class that I read the object from commits the transaction after it's read the object from the database and before it returns the object to the calling class.
I have tried various methods to work around this, and the simplest (for me) seems to be to try and access all of the collections in the object to ensure they are loaded before closing the transaction and returning the object.
The problem with that approach is that I don't know the structure of the object that I am retrieving from the database (part of the benefit of Hibernate for me) and therefore I can't call the appropriate methods to load the data. How do I overcome this? I don't really want to do eager fetching of the objects as they may be used by other applications. I don't want to use hbm files if I can avoid it.
This is the call to the Cache class:
Series series = (Series) Cache.getFromCache(id, Series.class)
In the Cache class:
public static Object getFromCache(String key, Class clazz) {
Object dbObject = HibernateUtil.loadObject(clazz, key);
if (dbObject != null) {
logger.debug("Cache (Get): Got object (" + clazz.getSimpleName() + ") for " + key);
return dbObject;
}
}
And HibernateUtil does:
public static Object loadObject(Class clazz, Serializable key) {
Session session = sessionFactory.getCurrentSession();
Object dbObject;
try {
session.beginTransaction();
dbObject = clazz.cast(session.get(clazz, key));
} finally {
session.getTransaction().commit();
}
return dbObject;
First of all, you could avoid a type cast by making your loadObject method parameterized:
public static <T> T loadObject(Class<T> clazz, Serializable key) {
Session session = sessionFactory.getCurrentSession();
T dbObject;
try {
session.beginTransaction();
dbObject = clazz.cast(session.get(clazz, key));
}
finally {
session.getTransaction().commit();
}
return dbObject;
}
Second: why do you open and commit the transaction in this method? Letting the caller open a transaction and commit it when he has finished using the object it has loaded from the cache would solve the problem.
Third: if you really want to let this method open and close the transaction, let it take an Initializer<T> instance as parameter. This initializer would have the responsibility to initialize all the necessary associations before returning the entity.
public static <T> T loadObject(Class<T> clazz,
Serializable key,
Initializer<T> initializer) {
Session session = sessionFactory.getCurrentSession();
T dbObject;
try {
session.beginTransaction();
dbObject = clazz.cast(session.get(clazz, key));
initializer.initialize(dbObject);
}
finally {
session.getTransaction().commit();
}
return dbObject;
}
In my code, I did as follows:
queried for a course entity
populate it with the given course data.
courseDao.update(entity) which internally calls persist(entity) method.
Surprisingly, the data is got updated successfully.
I am confused with this behaviour of persist method.
Please help me out.
code is as below:
//My Service......
#Service("myService")
#Transactional
public class MyServiceImpl implements MyService {
#Transactional(rollbackFor = { Throwable.class })
public void updateCourse(final Course course) throws MyServiceException {
------
------
CourseEntity courseEntity = courseDao.findById(course.getId());
populateCourseEntity(courseEntity, course);
courseDao.update(courseEntity);
}
}
//CourseDao.....
public class CourseDaoImpl implements CourseDao {
--------
public void update(final T entity) throws MyDaoException {
if (entity != null) {
this.entityManager.persist(entity);
}
else {
String errMsg = "Object to be updated cannot be null.";
throw new MyDaoException(errMsg);
}
}
}
When an entity is currently managed (attached to a session), all updates to it are directly reflected to the underlying storage even without calling persist().
In your case, you load your entity, so it's in the session. Then even if you don't call persist() it will be updated in the database on transaction commit.
The persist() description from the javadoc:
Make an entity instance managed and persistent.
This means that the method doesn't do anything in your case, since your entity is both persistent and managed.
P.S. Where I say "session", understand "entity manager"
JPA tries very hard to be a helpful API, such that anything you get from it (or save to it) will subsequently be tracked by JPA. This means than any further changes will be automatically handled for you by JPA without any additional work on your part.