So I have this method:
#Transactional
public void savePostTitle(Long postId, String title) {
Post post = postRepository.findOne(postId);
post.setTitle(title);
}
As per this post:
The save method serves no purpose. Even if we remove it, Hibernate
will still issue the UPDATE statement since the entity is managed and
any state change is propagated as long as the currently running
EntityManager is open.
and indeed the update statement is issued, but if I run the method without the #Transactional annotation:
public void savePostTitle(Long postId, String title) {
Post post = postRepository.findOne(postId);
post.setTitle(title);
}
Hibernate will not issue the update statement so one has to call postRepository.save(post);explicitly.
What is the difference between using #Transactional or not in this specific scenario?
In a standard configuration, the scope of a persistence context is bound to the transaction.
If you don't have an explicit transaction defined by means of the annotation your (non-existing) transaction span just the reading call to the database.
After that the entity just loaded is not managed.
This means changes to it won't get tracked nor saved.
Flushing won't help because there are no changes tracked.
Related
Following tutorial on Java Spring, I'm trying to understand how does #Transactional work with setters, and from other question/sources, I can't find a beginner-friendly explanation for it.
Let's say I have a user entity with getters and setters:
#Entity
public class User {
// Id set up
private Long id;
private String name;
private String email;
private String password;
// Other constructors, setters and getters
public void setName(String name) {
this.name = name;
}
}
And in the UserService I have a getUserName method:
#Service
public class UserService {
private final UserRepository userRepository;
#Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Transactional
public void getUserName(Long id) {
User user = userRepository.findById(id).orElseThrow();
user.setName("new user name"); // Why will this update db?
}
}
With #Transactional annotated, the setter function does update db, is this the spring way of updating data? Can someone help explain in layman term, how the Transactional work with setters under the hood?
Edit:
Without #Transactional, setter function won't update db, but in
order to mutate db, will have to call userRepository.save(user). And from the video, the instructor simply says the Transactional will handle jpql for us, and use setters along with it to update db.
Resource update:
Spring Transaction Management: #Transactional In-Depth, hope this is helpful.
Firstly, it is the underlying JPA provider (assume it is Hibernate) to be responsible for updating the entity but not Spring. Spring just provides the integration support with Hibernate.
To update an entity loaded from the DB , generally you need to make sure the following happens in order.
Begin a DB transaction
Use EntityManager to load the entity that you want to update.The loaded entity is said to be managed by this EntityManager such that it will keep track all the changes made on its state and will generate the necessary update SQL to update this entity in (4) automatically.
Make some changes to the entity 's state. You can do it through any means such as calling any methods on it , not just restricting to calling it by setter
Flush the EntityManager. It will then generate update SQL and send to DB.
Commit the DB transaction
Also note the followings:
Spring provides #Transactional which is a declarative way to execute (1) and (5) by annotating it to a method.
By default , Hibernate will call (4) automatically before executing (5) such that you do not need to call (4) explicitly.
Spring Data JPA repository internally use EntityManager to load the user. So the user return from the repository will be managed by this EntityManager.
So in short , #Transactional is necessary to update the entity. And updating the entity is nothing to do with setter as it just care if there are state changes on the entity in the end , and you can do it without using setter.
Spring uses Hibernate as ORM under the hood.
When you call userRepository.findById, Hibernate entity manager is called under the hood, it retrieves entity from database and at the same time makes this entity manageable (you can read separately about Hibernate managed entities).
What it means, in a simple words, the Hibernate 'remembers' the reference to this entity in its internal structures, in the so-called session. It, actually, 'remembers' all entities which it retrieves from database (even the list of entities obtained by queries) during single transaction (in the very basic case).
When you make some method #Transactional, by default Hibernate session is flushed when such method is finished. session.flush() is called under the hood.
Once session gets flushed, Hibernate pushes all changes made to these managed entities back into the database.
That is why your changes got to the database, once method was finished, without any additional calls.
To dig deeper into the topic, you can read more about Hibernate managed entities , session flush mode, repository.save(), repository.saveAndFlush() in Spring Data.
I've seen articles saying that we should try to limit the scope of transaction, e.g. instead of doing this:
#Transactional
public void save(User user) {
queryData();
addData();
updateData();
}
We should exclude queryData from the transaction by using Spring's TransactionTemplate (or just move it out of the transactional method):
#Autowired
private TransactionTemplate transactionTemplate;
public void save(final User user) {
queryData();
transactionTemplate.execute((status) => {
addData();
updateData();
return Boolean.TRUE;
})
}
But my understanding is that since JDBC will always need a transaction for all operations, if I use the second way, there will be 2 transactions opened and closed, 1 for queryData (opened by JDBC), and another for codes inside transactionTemplate.execute opened by our class. If so, won't this be a waste of resources now that you've split 1 transaction into 2?
If an transaction starts , it will use up one DB connection. So we generally want the transaction to be completed as fast as possible , and delay to start it as much as we can until we really need to access DB such that the connection pool has more time to provide more available connections for other requests to use.
So if part of the workflow within your function requires to take some time to finish their work and that work is not required to access DB, it is true that it is better to limit the scope of the transaction to exclude this part of the codes.
But in your example, as both transaction are executed in series and both need to access DB , I don't see there are any points to separate them into two different transactions.
Also, in term of Hibernate, it is very normal to load and update the entities in the same transaction such that you do not need to deal with the detached entities if the entities that you update are loaded from another already closed transaction. Dealing with detached entities is not easy if you are not familiar with Hibernate.
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?
It seems like a pretty basic question to me, so probably I'm either lacking the right search terms or I'm completely missing something about the way how managed entites work, but nevertheless I was unable to find out how to do this: Writing new attribute values of a managed entity to the database in a transactional way, meaning I want to set a bunch of values to an entity bean and have them persisted all at once and without other threads seeing a „dirty“ intermediate state of the bean or interrupting the writing process.
This is the entity class:
#Entity
public class MyEntityClass
{
...
private String status;
private String value;
...
public void setStatus(String status)
{
...
public void setValue(String vlaue)
{
...
}
I'm using it here:
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyService
{
#PersistenceContext
protected EntityManager entityManager;
...
MyEntityClass entity = entityManager.find(entityClass, id);
//Now I'd like to set some attributes in a transactional way
entity.setStatus(newStatus);
//what if the new status got persisted and another thread reads from the database now
entity.setValue(newValue);
//flushing, just to make sure that at least from here on the database is in a consistent state
entityManager.flush();
}
I need to ensure that no other thread can see the entity in a „half-written“ state, i.e. with the new status already persisted but the old value still present in the database. I tried using a lock:
...
MyEntityClass entity = entityManager.find(entityClass, id);
entityManager.lock(entity, LockModeType.WRITE);
entity.setStatus(newStatus);
entity.SetValue(newValue);
entityManager.flush();
entityManager.lock(entity, LockTypeMode.NONE);
...
But this throws:
java.lang.IllegalArgumentException: entity not in the persistence context
because the transaction used for reading the entity ends after the entityManager.find() is completed and therefore also the persistence context is gone, as I learned from this answer.
Also, I read here that I cannot manually create transactions with an EntityManager that uses container-managed transactions. But isn't there any way now to manually ensure that the entity's attributes are persisted together (or not at all)? Or is this somehow already done automatically?
Since you run in EJB container and your transaction is managed by the container, every business method of your EJB bean is invoked inside a transaction, so as far as your isolation level of the transaction is no set to Read uncommitted, changes that where made in the method will be visible when the transaction commits, that is when the method finishes.
UPDATE:
As you don't use business methods it looks like your code is running without any transaction. In this case, every invocation of EnitytMangers method will create a new PersistenceContext.
This is why you are getting exception. Entity that you get form find method is detached when next method of EntityManager is called (as new PersistenceContext is created).
I am using Spring with Hibernate. I am running jUnit test like this:
String number = invoiceNumberService.nextInvoiceNumber();
and invoiceNumberService method is:
InvoiceNumber invoiceNumber = invoiceNumberRepository.findOne(1L);
it is using simple spring data repository method, and it's working well. But when I override this method to use locking:
#Lock(LockModeType.PESSIMISTIC_READ)
#Override
InvoiceNumber findOne(Long id);
I am getting "javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction"
I can't understand why its optimistic lock exception, while I am using pessimistic locking? And where is this part when another transaction is changing this entity?
I have already dig a lot similar questions and I am quite desperate about this. Thanks for any help
Solution:
The problem was in my init function in test class:
#Before
public void init() {
InvoiceNumber invoiceNumber = new InvoiceNumber(1);
em.persist(invoiceNumber);
em.flush();
}
There was lack of
em.flush();
Which saves the data into database, so findOne() can now retreive it
Question: Have you given the #transcational annotation in dao or service layer?
it happens due to the reason that two transaction is simultaneously trying to change the data of same table..So if you remove all the annotation from the dao layer and put in the service layer it should solve the problem i this..because i faced similar kind of problem.
Hope it helps.
Just for the sake of it I'll post the following, if any one disagrees please correct me. In general in java you are advised to use Spring/hibernate and JPA. Hibernate implements JPA so you will need dependencies for Spring and Hibernate.
Next let Spring/hibernate manage your transactions and the committing part. It is bad practice to flush/commit your data yourself.
For instance let assume the following method:
public void changeName(long id, String newName) {
CustomEntity entity = dao.find(id);
entity.setName(newName);
}
Nothing will happen after this method (you could call merge and commit). But if you annotate it with #Transactional, your entity will be managed and at the end of the #Transactional method Spring/hibernate will commit your changes. So this is enough:
#Transactional
public void changeName(long id, String newName) {
CustomEntity entity = dao.find(id);
entity.setName(newName);
}
No need to call flush, Spring/Hibernate will handle all the mess for you. Just don't forget that your tests have to call #Transactional methods, or should be #Transactional themselves.