Spring-Data Calling Save on Entity from Controller - java

I am using Spring Data within Spring Boot. I have a simple controller method that accepts an instance of an entity I'd like to update in my database. However, after calling repository.save() the object isn't persisted, however, if I query for the object (from the Id of the incoming entity) I have no problem, and the update persists.
Also the HTTP response is a 405 that the method doesn't work?
Controller:
#Transactional
public class AccountController {
#Autowired
private AccountService accountService;
#RequestMapping(value = "/account/{id}", method = RequestMethod.PUT)
#ResponseStatus(value = HttpStatus.OK)
public #ResponseBody Account updateAccount(#PathVariable("id") String id, #RequestBody Account account) {
return accountService.updateAndValidateAccountProfile(account);
}
}
Service:
#Override
public Account updateAndValidateAccountProfile(Account account) {
// calling findOne() returns the object ... and the save() works fine
//Account currentAccount = accountRepository.findOne(account.getId());
return accountRepository.save(account);
}
Repository:
interface AccountRepository extends CrudRepository<Account, Long> {
}
Is there something about the way I need to identify the object to Spring Data. I see the select statements fire out of hibernate, and I know the merge should happened on the primary key (id) within the Spring Data call to .save() - so I shouldn't need to query the object and manually assign the updated values correct??

Have you enabled transaction management? You need to use #EnableTransactionManagement on your config class otherwise #Transactional has no effect.
A spring tutorial on Managing Transactions
As a side note: making the controller layer transactional is usually not a good idea, especially when you have a proper separation of controller, service and repository layers. Consider moving #Transactional into the service layer.
Since you are using JPA, also make sure you have a JpaTransactionManager configured:
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
You might try extending the JpaRepository interface instead of CrudRepository then you can use saveAndFlush(entity) to make sure the changes are immediately flushed after merge.

Related

confused about the jpa entitymanager and lazyload

I created a user management service based on springboot.
The user will have a list of attachments, so the relationship of the user and attachment is OneToMany.
I ignored the insert logic here since my question is about the lazyload and when the entitymanager is opened and closed. Below is the entity, controller, service, dao, repository related code.
Entity
#Entity
#Table(name="User")
public class UserInfoEntity {
private long id;
private String mail;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "userInfoEntity", cascade = CascadeType.ALL)
private List<UserAttachmentEntity> attachmentList = new ArrayList<>();
}
#Entity
#Table(name = "ATTACHMENT")
public class UserAttachmentEntity {
private long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="userRequestId")
private UserInfoEntity userInfoEntity;
}
Service
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDao userDao;
#Override
#Transactional
public void save(UserInfoEntity userInfoEntity) throws RestException {
userDao.save(userInfoEntity);
}
#Override
// I did set #Transactional here
public UserInfoEntity findByMail(String mail) {
UserInfoEntity userInfoEntity = userDao.findByMail(mail);
return userInfoEntity;
}
}
DAO
#Service
public class UserDaoImpl implements UserDao {
#PersistenceContext
private EntityManager entityManager;
#Autowired
private UserInfoEntityRepository userInfoRepository;
#Override
public UserInfoEntity findByMail(String mail) {
return userInfoRepository.findFirstByMailOrderByIdDesc(mail);
}
}
Repository
#Repository
public interface UserInfoEntityRepository extends JpaRepository<UserInfoEntity, Integer> {
UserInfoEntity findFirstByMailOrderByIdDesc(String mail);
}
Controller
#Controller
#RequestMapping(value = "/user")
public class UserController {
#RequestMapping(value = "load", method = RequestMethod.POST)
#ResponseBody
public UserInfoEntity load(UserInfoEntity userInfoEntityInput, HttpServletRequest request) throws Exception {
UserInfoEntity userInfoEntity = userService.findByMail(userInfoEntityInput.getMail());
System.out.println(userInfoEntity.getAttachmentList());
return userInfoEntity;
}
}
After I test the load method in controller, I found that even I set the
#OneToMany(fetch = FetchType.LAZY,...) in
UserInfoEntity.
I can still call userInfoEntity.getAttachmentList() in controller.
(I can see the select * from attachment query was printed there)
The answer of the post lazyinitializationexception-in-spring-data-jpa said that you need to fetch the lazy data while you are inside of a transaction.
But I did not set the #Transaction in the findByMail method in service.
And I remember that I also met this exception a couple of days ago. But now I can load the lazy data successfully in controller.
I mainly have below questions.
When the entitymanager is opened and closed? (is it opened in service or dao?)
Why the lazy data can be loaded in controller?
Is the entity manager thread-safe? (I googled, but not find useful answer)
Is the entity manager singleton? ( in above code, I inject the entity manager into dao, although I did not use it, I used spring data, I can inject the entity manager to service, controller, find the hashcode is different)
Thanks in advance. I wrote this question in company, I was not allowed to push any code to github in company. If I can, I think it will be more convenient for you since the spring boot project with h2 database is very easy to setup locally.
Spring Boot JPA Opens Session in View by default
If you go along the Appendix A. Common application properties, you will find by default Spring boot defines
spring.jpa.open-in-view=true
That actually register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request.
This actually leads to confusion, See this issue reported by the community.
When the EntityManager is opened and closed?
Basically, an EntityManager opens a transaction where #Transaction annotation is declared or in your case it is opened by the OpenEntityManagerViewInterceptor on every request and that stays open before the response is made.
Is the entity manager thread-safe?
NO. Its not threadsafe, but the EntityManagerFactory is. And EntityManager is obtained from EntityManagerFatory. Hence an EntityManager is inexpensive and expected to be used once for a single unit of work. Where An EntityManagerFactory is typically created at application initialization time and closed at the application end. See Hibernate doc for details about Obtaining an EntityManager in a Java SE environment
Is the entity manager singleton?
No. EntityManager is an interface, and what gets injected in the spring bean when you autowire, is not the entity manager itself but a context-aware proxy that will delegate to a concrete entity manager at runtime. Usually, the concrete class used for the proxy is SharedEntityManagerInvocationHandler
For more detail explanation about how the whole JPA transaction go around. I recommend reading this How Does Spring #Transactional Really Work?
In Spring Boot the Open Session In View is enabled by default which means that you don't have to use #Transaction in your code unless it is required by your app's logic.
However, note that the enabled OSIV is considered as bad practice - the detailed debate about this topic can be found at https://stackoverflow.com/a/37526397/3750108

Is it possible to have "connection preparation" with Spring / JPA persistency

I have a Spring CrudRepository that is just an interface, and I have a persistence context class where I have defined my data source:
#Configuration
#EnableTransactionManagement
public class PersistenceContext {
#Bean(name="dataSource", destroyMethod = "close")
public DataSource dataSource() throws SQLException {
return ...
public interface DataRepository extends CrudRepository<Data, Long> {
Data findById(long id);
}
Then
#Autowired
protected DataRepository repository;
...
Data data = repository.findById(1234);
Everything works fine but the database model is such that I actually need to call a stored procedure on the same connection before calling findById from the using code. This procedure must take a parameter that a calling code will know but it will differ between calls so it is not possible just to override DataSource.getConnection and return the "prepared connection" there.
Is it any way to "prepare a connection" before making an access code to the Spring repository?
Using AOP would seem to be one approach: an example of using AOP to enrich Spring Data repositories can be found at the below:
https://github.com/spring-projects/spring-data-jpa-examples
If you can get a reference to the injected EntityManager within the advice then you should be able to get the underlying connection from that using one of the methods detailed here:
How can i get the session object if i have the entitymanager
To get a reference to the EntityManager you may have to create a custom repository from which all your repositories inherit:
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories

Manage rollback on transaction in my managed bean

I am using JPA/hibernate, Spring and JSF.
so my application is organized as following:
I have my entities,
My Dao Interface and implementation for each entity where I define basic methods: findById, add, update, remove ...
and then I have my service layer which just use DAO interfaces and where ther is basically the same methods as in my DAO.
My problem is that in my backing bean, I have a method Add_hospital(), which add a hospital and also services in that hospital, so my method looks like
add_hospital(){
add-hospital();
add-services();
add-Hospital-schedule();
}
so this method is a transaction and I want that if some issue happen, the transaction rollback, but I know that the rollback need to be managed in my DAO, will I need to define my method Add_hospital() in my managed bean, and it's in this stage where I have this combination of inserts.
Please how to solve this problem?
Transactions should be managed on the service layer, not data access.
Example from spring:
#Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}

One Transaction for Hibernate Validation and Spring controller

I am trying to implement the registration controller for a Rest API. I have read about where to place #Transactional quite a bit. (Not at DAO level but at the services maybe orchestrated). In my use case I want not only services but also a hibernate validation to use the same transaction.
This is the code of the controller:
#Autowired
private UserService userService;
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
#Transactional
public DefaultResponse register(#Valid RegisterIO registerIO, BindingResult errors) {
DefaultResponse result = new DefaultResponse();
if (errors.hasErrors()) {
result.addErrors(errors);
} else {
userService.register(registerIO);
}
return result;
}
I have written an custom contraint annotation, which validates an attribute of the parameter registerIO. Both, this validator and userService.register(registerIO); access the database (check if the email address is already in use).
Therefore I want both methods use the same Hibernate session and transaction.
This approach results in the following exception:
org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:941)
The problem is the #Transactional annotation. When I place this annotation at the methods witch call the database everything works find but two transactions are startet. I suspect that when i place it at the register Method the hibernate validation is performed before #Transactional starts the transaction for this method.
I developed the following functional workaround but I am not happy with it. This codes does not use the #Valid annotation but calls the validator by itself:
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
#Transactional
public DefaultResponse register( RegisterIO registerIO, BindingResult errors) {
DefaultResponse result = new DefaultResponse();
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Validator validator = vf.getValidator();
Set<ConstraintViolation<RegisterIO>> valResult = validator.validate(registerIO);
I try to summarise my question:
Using Spring MVC and Hibernate-Validation together with #Valid and #Transactional, how is it possible to encapsulate the whole request into one transaction?
Thank you :)
Your workaround could be improved by using a single Validator and injecting it intp the controller. Have you tried:
#Autowired
private Validator validator;
This way you skip the overhead of creating the validator on each request.
You should also be careful with race conditions. While you are checking the database whether a given email exists another request can create this record, so that you still get an exception at the time you insert the data.

Mixing declarative and programmatic transactions with Spring and JPA listeners

I'm using a JPA EntityListener to do some additional audit work and am injecting a Spring-managed AuditService into my AuditEntryListener using #Configurable. The AuditService generates a collection of AuditEntry objects. The AuditService is itself a Singleton scoped bean, and I'd like to gather all the AuditEntry objects under a common key that can then be accessed by the outermost service layer (the one that invoked the persist call which in turn triggered the EntityListener).
I'm looking at using Spring's TransactionSynchronizationManager to set a specific transaction name (using UID() or some other unique strategy) at the beginning of the transaction, and then using that name as a key within the AuditService that will allow me to group all AuditEntry objects created within that transaction.
Is mixing declarative and programmatic transaction management have the potential for trouble? (Though I'm doing nothing more than setting the transaction name). Is there a better way to associate the generated AuditEntry objects with the current transaction? This solution does work for me, but given that the TransactionSynchronizationManager isn't intended for application use, I'd like to make sure that my use of it won't cause some unforseen problems.
Related Question
Finally, a related, but not immediately pertinent question: I know that the documentation for JPA EntityListeners cautions against using the current EntityManager, but if I did want to use it to diff an object against it's persisted self, would I be safe using an #Transactional(propagation=REQUIRES_NEW) annotation around my preUpdate() method?
Prototype Code:
Service Class
#Transactional
public void create(MyEntity e) {
TransactionSynchronizationManager.setCurrentTransactionName(new UID().toString());
this.em.persist(e);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
Set<AuditEntry> entries = auditService.getAuditEntries(TransactionSynchronizationManager.getCurrentTransactionName());
if(entries != null) {
for(AuditEntry entry : entries) {
//do some stuff....
LOG.info(entry.toString());
}
}
}
});
}
JPA EntityListener
#Configurable
public class AuditEntryListener {
#Autowired
private AuditService service;
#PreUpdate
public void preUpdate(Object entity) {
service.auditUpdate(TransactionSynchronizationManager.getCurrentTransactionName(), entity);
}
public void setService(AuditService service) {
this.service = service;
}
public AuditService getService() {
return service;
}
}
AuditService
#Service
public class AuditService {
private Map<String, Set<AuditEntry>> auditEntryMap = new HashMap<String, Set<AuditEntry>>();
public void auditUpdate(String key, Object entity) {
// do some audit work
// add audit entries to map
this.auditEntryMap.get(key).add(ae);
}
}
#Filip
As far as I understand, your requirement is:
Have an unique token generated within each transaction (database
transaction of course)
Keep this unique token easily accessible across all layers
So naturally you're thinking about the TransactionSynchronizationManager provided by Spring as a facility to store the unique token (in this case, an UID)
Be very carefull with this approach, the TransactionSynchronizationManager is the main storage helper to manage all the #Transactional processing for Spring. Under the #Transactional hood, Spring is creating an appropriate EntityManager, an appropriate Synchronization object and attach them to a thread local using TransactionSynchronizationManager.
In your service class code, inside a #Transactional method your are tampering with the Synchronization object, it can end up with undesirable behavior.
I've done an indept analysis of how #Transactional works here, have a look: http://doanduyhai.wordpress.com/2011/11/20/spring-transactional-explained/
Now back to your needs. What you can do is:
Add a Thread local to the AuditService, containing the unique token when entering the #Transactional method and destroy it when exiting the method. Within this method call, you can access the unique token in any layer. Explanation for ThreadLocal usage can be found here: http://doanduyhai.wordpress.com/2011/12/04/threadlocal-explained/
Create a new annotation, let's say #Auditable(uid="AuditScenario1") to annotate methods that need to be audited and use Spring AOP to intercept these method calls and manage the Thread local processing for you
Example:
Modified AuditService
#Service
public class AuditService {
public uidThreadLocal = new ThreadLocal<String>();
...
...
}
Auditable annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#Documented
public #interface Auditable
{
String uid();
}
Usage of #Auditable annotation
#Auditable(uid="AuditScenario1")
#Transactional
public void myMethod()
{
// Something
}
Spring AOP part
#Around("execution(public * *(..)) && #annotation(auditableAnnotation))
public Object manageAuditToken(ProceedingJoinPoint jp, Auditable auditableAnnotation)
{
...
...
AuditService.uidThreadLocal.set(auditableAnnotation.uid())...
...
}
Hope this will help.
You can come up with a solution using the TransactionSynchronizationManager. We register a "TransactionInterceptorEntityListener" with JPA as an entity-listener. What we wanted to achieve is the ability to listen to CRUD events such that we can work with a spring managed "listener" that has a lifecycle tied to the current transaction (i.e., spring-managed but instance per transaction). We sub-class the JPATransactionManager and introduce in the prepareSynchronization() method, a hook to setup a "TransactionInterceptorSynchronizer." We also use the same hook for allow code (in programmatic tx) to associate and retrieve arbitrary objects with the current transaction and also register jobs that run before/after transaction commit.
The overall code is complex, but definitely do-able. If you use JPATemplates for programmatic tx, it is tough to achieve this. So we rolled our own template that simply calls the JPA template after taking care of the interceptor work. We plan to open-source our JPA library (written on top of Spring's classes) soon.
You can see a pattern of adding custom transactions and hooks with Spring managed transactions in the following library for Postgresql

Categories

Resources