Explain clearly what's #Transactional doing, but I still don't understand when should Spring Boot methods use this annotation:
For example:
I have this method:
void addPerson () {// code that calls the DAO layer}
My method will work well without the #Transactional annotation then why i should add this annotation.
More precisely (in spring boot) what's the difference between:
#Transactional void addPerson () {// code that calls the DAO layer}
and
void addPerson () {// code that calls the DAO layer}
Or does Spring boot add automatically this annotation so we don't need to add it to our services
You use #Transcational when concurrent calls on your API can affect each other.
Let's say you want to add a Person (you retreive data from somewhere, create a new Person from data and add it to a list of persons). Let's assume in order to create a Person you need a partner attribute which is another Person.
Before creating a Person you would search the partner by Id somehwere and add it to the new Person partner attribute. But what if during all this Queries the partneryou wanted to add is deleted somewhere (let's say in the database due to some other query). You'll end up not having the object you requested.
If you use #Transactional Spring makes sure all the required data is safe until the Transaction ends. Once it ends, the delete request from the partner would take place and then you'll have some logic to remove it from the new Person object. But that would take place afterwards.
You use #Transactional to ensure safety on your "Transactions".
Related
I have an List which contains say 4 DTOs. I am performing some processes on each of the DTOs present in my list. If suppose for one the DTO, any exception comes then all the transactions are rolled back (even if the process is success for other 3 DTOs).
My code looks like this :
#Transactional
public void processEvent(List<MyObject> myList){
myList.forEach(dto -> process(dto));
}
public void process(MyObject dto){
//some code which calls another class marked as #Transactional
// and save the data processed to database
}
I want to perform these processes for each DTO on a sepearte thread such that exception encountered in one thread does not rollbacks transaction for all the DTOs.
Also is there a way to process these DTOs one by one on different threads so that data consistency is maintained ?
Simply move the transactional to the method called with the dto, plus I am not sure if it is needed a transaction for dto. This looks as a controller which should not have any transactional annotaions. In the service once you change the dto to entity and are ready to save it you may put the anotation. Furthermore if you are simply calling the repository's save method you do not need to be in transaction as save method has the annotation in the repository.
public void processEvent(List<MyObject> myList){
myList.forEach(dto -> process(dto));
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void process(MyObject dto){
//some code which calls another class marked as #Transactional
// and save the data processed to database
}
And one last advice do not put #Transactional on classes, except if they have the readOnly parameter set to true. Then you can put #Transactional on the methods that perform any CRUD operations.
I'm creating a website for a school project which uses spring for the backend. I'm trying to insert data into the database when new data is saved to a specific table.
I've tried using #HandleAfterCreate and #PrePersist, but neither worked. I'm not very experienced with spring. The teacher told us to use it and now I don't know what do.
#HandleAfterCreate
public void handlePersonBeforeCreate(Person person){
logger.info("Inside Person Before Create....");
Set<Qualifikation> qualifikationen = new HashSet<>();
kompetenzRepository.findAll().forEach(kompetenz -> {
Qualifikation qualifikation = new Qualifikation();
qualifikation.setAusmass(0);
qualifikation.setKompetenz(kompetenz);
qualifikation.setPerson(person);
});
person.setQualifikationen(qualifikationen);
System.out.println(person.getDisplayName());
}
The code should set a person's "Qualifikation" to a default value when the person is inserted (via OAuth login). It should have every "Kompetenz" with a value of 0 by default. Kompetenz has a 1 to n relation to Qualifikation. If you need more information please ask me.
It looks like you're trying to have access to the repository layer of your application inside an entity. This is generally not a good idea, as the entities should only know about the data they hold, not the other application components.
In this particular case it would be wise to use a #Service class with a method that you can call to insert the data into the database. In the method you could then insert any other entities as well. Let your repositories be fields of the service and make them #Autowired.
I think you need to enable JPA auditing . It can be enabled in Spring by add #EnableJpaAuditing to your persistence configuration. This tells Spring to listen JPA entity lifecycle events and call the annotated methods in appropriate places.
Also I think you should make the callback method private if it is meant to be called only when persisted (#PrePersist).
See details here. In this article is also presented entity listeners which might also be a good solution when dealing with multiple entities having a need for same pre-persist functionality.
I think you should create a service class, a repository class and an entity which will be stored through repository. The logic of getting all inner elements and filling it with default value is to be written in service and not a good idea to write in entity class.
If you need any help regarding it, let me know .
Welcome to community!!
I have the following question. From what I understand the #Transactional annotation is supposed to keep the session alive, thus enabling to lazy fetch child entities without the need to performe a specific joining query.
I have the following scenario where I do not understand why I'm still getting a LazyInitializationException.
My app runs a resolver in order to provide the various controller services with a resolved object so that it can be used directly.
Said resolver intercepts a header from the request and using it's value attempts to query the db in order to fetch the object. Now the object in question is quite simple is it's doings albeit it has a list of two sub-entities.
In order to perform the resolving action I'm using an extra service where I basically wrap some JpaRepository methods. The complete is below:
#Service
public class AppClientServiceImpl implements AppClientService {
private static final Logger LOGGER = LoggerFactory.getLogger(AppClientServiceImpl.class.getCanonicalName());
private final AppClientRepository repository;
#Autowired
public AppClientServiceImpl(AppClientRepository repository) {
this.repository = repository;
}
#Override
#Transactional(readOnly = true)
public AppClient getByAppClientId(final String appClientId) {
LOGGER.debug("Attempting to retrieve appClient with id:: {}", appClientId);
return repository.findByAppClientId(appClientId);
}
#Override
#Transactional
public void saveAndFlush(final AppClient appClient) {
LOGGER.debug("Attempting to save/update appClient:: {}", appClient);
repository.saveAndFlush(appClient);
}
}
As you can see both methods are annotated as #Transactional meaning that the should keep the session alive in the context of that said method.
Now, my main questions are the following:
1) Using the debugger I'm seeing even on that level getByAppClientId the list containing on the sub-entities which is lazy loaded has been resolved just fine.
2) On the resolver itself, where the object has been received from the delegating method, the list fails to be evaluated due to a LazyInitializationException.
3) Finally on the final controller service method which is also marked as #Transactional, the same as above occurs meaning that this eventually fails to it's job (since it's performing a get of the list that has failed to initialize.
Based on all the above, I would like to know what is the best approach in handling this. For once I do not want to use an Eager fetching type and I would also like to avoid using fetch queries. Also marking my resolver as #Transactional thus keeping the session open there as well is also out of the question.
I though that since the #Transactional would keep the session open, thus enabling the final service method to obtain the list of sub-entities. This seems not to be the case.
Based on all the above it seems that I need a way for the final service method that gets call (which needs the list on hand) to fetch it somehow.
What would the best approach to handle this? I've read quite a few posts here, but I cannot make out which is the most accepted methods as of Spring boot 2.0 and hibernate 5.
Update:
Seems that annotating the sub-entitie with the following:
#Fetch(FetchMode.SELECT)
#LazyCollection(LazyCollectionOption.TRUE)
Resolves the problem but I still don't know whether this is the best approach.
You initialize the collection by debugging. The debugger usually represents collections in a special way by using the collection methods which trigger the initialization, so that might be the reason why it seems to work fine during debugging. I suppose the resolver runs outside of the scope of the getByAppClientId? At that point the session is closed which is why you see the exception.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(AppClient.class)
interface AppClientDto {
String getName();
}
Querying could look like this
List<AppClientDto> dtos = entityViewManager.applySetting(
EntityViewSetting.create(AppClientDto.class),
criteriaBuilderFactory.create(em, AppClient.class)
).getResultList();
I am trying to understand the behavior of transaction propagation using SpringJTA - JPA - Hibernate.
Essentially I am trying to update an entity. To do so I have written a test method where I fetch an object using entity manager (em) find method ( so now this object is manged object). Update the attributes of the fetched object. And then optionally make a call to service layer(service layer propagation=required) which is calling em.merge
Now I have three variations here :
Test method has no transactional annotation. Update the attributes
of the fetched object and make no call to service layer.
1.1. Result level 1 cache doesn't gets updated and no update to DB.
Test method has no transactional annotation. Update the attributes of the fetched object. Call the service layer.
2.1. Result level 1 cache and DB gets updated.
Test method has Transnational annotation which could be any of the following. Please see the table below for Propagation value at the test method and the outcome of a service call.
(service layer propagation=required)
So to read the above table, the row 1 says if the Test method has transaction propagation= REQUIRED and if a service layer call is made then the result is update to Level 1 cache but not to the DB
Below is my test case
#Test
public void testUpdateCategory() {
//Get the object via entity manager
Category rootAChild1 = categoryService.find(TestCaseConstants.CategoryConstant.rootAChild1PK);
assertNotNull(rootAChild1);
rootAChild1.setName(TestCaseConstants.CategoryConstant.rootAChild1 + "_updated");
// OPTIONALLY call update
categoryService.update(rootAChild1);
//Get the object via entity manager. I believe this time object is fetched from L1 cache. As DB doesn't get updated but test case passes
Category rootAChild1Updated = categoryService.find(TestCaseConstants.CategoryConstant.rootAChild1PK);
assertNotNull(rootAChild1Updated);
assertEquals(TestCaseConstants.CategoryConstant.rootAChild1 + "_updated", rootAChild1Updated.getName());
List<Category> categories = rootAChild1Updated.getCategories();
assertNotNull(categories);
assertEquals(TestCaseConstants.CategoryConstant.rootAChild1_Child1,categories.get(0).getName());
}
Service Layer
#Service
public class CategoryServiceImpl implements CategoryService {
#Transactional
#Override
public void update(Category category) {
categoryDao.update(category);
}
}
DAO
#Repository
public class CategoryDaoImpl {
#Override
public void update(Category category) {
em.merge(category);
}
}
Question
Can someone please explain why does REQUIRED, REQUIRES_NEW, and NESTED doesn't lead to insertion in the DB?
And why absence of transaction annotation on Test case lead to insertion in the DB as presented in my three variations?
Thanks
The effect you're seeing for REQUIRED, NESTED, and REQUIRES_NEW is due to the fact that you're checking for updates too early
(I'm assuming here that you check for db changes at the same moment when the test method reaches the assertions, or that you roll the test method transaction back somehow after executing the test)
Simply enough, your assertions are still within the context created by the #Transactional annotation in the test method. Consequently, the implicit flush to the db has not been invoked yet.
In the other three cases, the #Transactional annotation on the test method does not start a transaction for the service method to join. As a result, the transaction only spans the execution of the service method, and the flush occurs before your assertions are tested.
I have a static method in the entity
#Transactional
public static void updateState() {
entityManager().createNativeQuery("UPDATE TABLEA SET hide = 1 WHERE id= 1").executeUpdate();
}
But when I call the method, I catch a exception say the update statement need a transaction.
Am I using the #Transactional in the wrong way?
It seems like you are trying to make your Entity a fat domain model (as opposed to thin models that are most common in the Java EE world) following the Active Record pattern.
What you are trying to do will not work as is in Spring.
If you refactor your method to not be static (first problem) then one way to get #Transactional working on a JPA entity is to use the #Configurable annotation from Spring (making it managed by Spring - therefore fixing the second problem), along with load time weaving and a Java agent. See this and this for more details.
Maybe you should try with the annotation:
#Transactional(readOnly=false)