Spring data JPA locking - spring

I need to use #Lock inside of my implementations:
#Lock(LockModeType.PESSIMISTIC_WRITE)
private Note findOneForUpdate(BigInteger id) {
return noteDao.findOne(id);
}
But other sources say it should be in interfaces:
#Repository
public interface NoteRepository extends JpaRepository<Note, BigInteger>, NoteDao {
#Lock(LockModeType.PESSIMISTIC_WRITE)
Note findOne(BigInteger id);
}
So, is first option possible? I tried it with spring-boot-starter-data-jpa 1.5.3.RELEASE, but lock did not work.

#Lock annotation is required in repository class
#Lock(LockModeType.PESSIMISTIC_WRITE) // not required
private Note findOneForUpdate(BigInteger id) {
return noteDao.findOne(id);
}
#Repository
public interface NoteRepository extends JpaRepository<Note, BigInteger>, NoteDao {
#Lock(LockModeType.PESSIMISTIC_WRITE) // required
Note findOne(BigInteger id);
}

Related

Spring TransactionManager behavior with Spring Data and JpaRepository

I have a controller which does the following
A submit end point which save an entry in db and then call some external service asynchronously
Track the update of asynchronous call (this call updates an associated table) by watching the db and update the status of the entry created in step one
I was using the #Query Annotation to verify if step one entry exist in db and it was always returning empty. I tried changing it to the default spring method and it starts returning the inserted value.
I read about proxies, #Transactional and how non CRUD methods in a JPARepository are non transactional and tried few things like transaction propagation and self injection and even explicitly marking the repo method #Transactional. But none of them fixed the issue. Using spring data method solved it but I still don't understand what happened. Can someone help with an explanation of this behavior.
Basic code snippet is below
MyController
#RestController
public class MyController {
private final MyService myService;
private final MyRepository myRepository;
#Autowired
public MyController(MyService myService,
MyRepository myRepository) {
this.myService = myService;
this.myRepository = myRepository;
}
#PostMapping(value = "/submit")
public ResponseEntity<MyResponse> submit(#Valid #RequestBody MyRequest myRequest) {
return ResponseEntity
.accepted()
.body(MyResponse.success(myService.submit(myRequest), "SUBMITTED"));
}
/**
* This method is to update the status of the entry created by /submit endpoint
* if the asynchoronous process triggered by submit endpoint update an associated table
*/
#PostConstruct
private void trackUpdates() {
..
someObserver.subscribe(trackedAssociatedEntity -> {
myService.trackAndUpdateBasedOnAssociatedEntity(trackedAssociatedEntity);
});
}
}
MyService
#Service
#Transactional
public class MyService {
private final MyRepository myRepository;
#Autowired
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
submit(MyRequest myRequest) {
myRepository.save(myEntity);
//makes that asynchronous call
}
public void trackAndUpdateBasedOnAssociatedEntity(#NotNull MyAssociatedEntity myassociatedEntity) {
// This commented call always return empty but the uncommented code works as expected
// List<MyEntity> existingEntity =
// myRepository.findEntityByField1AndField2(myassociatedEntity.getField1(),myassociatedEntity.getField2());
List<MyEntity> existingEntities =
myRepository.findByField1AndField2(myassociatedEntity.getField1(),myassociatedEntity.getField2());
if(existingEntities.isEmpty()){
//create new
}else{
//update
}
}
}
}
}
MyRepository
#Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query("SELECT e FROM MyEntity e WHERE e.field1 = ':field1' and e.field2 = ':field2' ")
List<MyEntity> findEntityByField1AndField2(String field1, String field2);
List<MyEntity> findByField1AndField2(String field1, String field2);
}
I believe that '' are not needed. Please try the following:
#Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query("SELECT e FROM MyEntity e WHERE e.field1 = :field1 and e.field2 = :field2")
List<MyEntity> findEntityByField1AndField2(String field1, String field2);
List<MyEntity> findByField1AndField2(String field1, String field2);
}

Spring Data - PagingAndSortingRepository with custom query (HQL)?

Trying to mix PagingAndSortingRepository with custom queries, no luck..
Custom repo:
public interface SiteRepositoryCustom
{
public List<SitesDbRecord> getActiveSites();
}
Impl repo:
#Repository
public class SiteRepositoryImpl implements SiteRepositoryCustom
{
private static final Logger logger = ...
#PersistenceContext
private EntityManager em;
#Override
public List<SitesDbRecord> getActiveSites()
{
logger.info( "getActiveSites start" );
try
{
String hql = "select s from SitesDbRecord s where s.isActive = true";
return em.createQuery( hql ).setMaxResults( Integer.MAX_VALUE ).getResultList();
}
catch ( Exception e )
{
logger.error( "getActiveSites failed.", e );
return null;
}
}
}
The repo injected to the service:
public interface SiteRepository extends PagingAndSortingRepository<SitesDbRecord, Integer>, SiteRepositoryCustom {
public List<SitesDbRecord> getActiveSites( Pageable pageable );
public List<SitesDbRecord> getActiveSites();
}
If I just extend CrudRepository (without the Pageable method) then all is OK. Trying to extend PagingAndSortingRepository (with or without the Pageable method) then Spring fails to boot with
PropertyReferenceException: No property getActiveSites found for type SitesDbRecord!
What is the correct way to use PagingAndSortingRepository with custom queries? Probably got it wrong, but I assumed it's Spring responsibility to provide the handling of paging/sorting.
If SitesDbRecord has boolean property named active it should be:
public interface SiteRepository extends PagingAndSortingRepository<SitesDbRecord, Integer> {
public List<SitesDbRecord> findByActiveIsTrue( Pageable pageable );
public List<SitesDbRecord> findByActiveIsTrue();
}
There is no need to extend your Custom repository, just implement PagingAndSortingRepository

Query and Database performance when used with JpaRepository findAll() vs native query using JpaRepository

I am developing a spring boot project where i am having two functions for JPA on which i need to figure out which function will perform better and put less pressure on database query performance and utilise Hibernate caching. Please guide on which query to use.
My Repository interface:
#Repository
public interface CustomersRepository
extends JpaRepository<CustomersEntity, Long> {
#Query(nativeQuery = true, value = "SELECT * FROM customers WHERE c_mobile = ?1")
CustomersEntity findcustomerByMobile(String mobileNo);
#Override
List<CustomersEntity> findAll();
}
My Service class:
#Scope("request")
#Service
public class CustomerServiceImpl implements ICustomerService {
#Autowired
private CustomersRepository customersRepository;
#Override
public boolean findCustomerByMobile1(long mobileNo) {
CustomersEntity customersEntity = customersRepository.findcustomerByMobile(mobileNo);
if (customersEntity != null)
return true;
else
return false;
}
#Override
public boolean findCustomerByMobile2(long mobileNo) {
List<CustomersEntity> entityList = customersRepository.findAll();
for (CustomersEntity entity : entityList) {
if (entity.getcMobile() == mobileNo) {
return true;
}
}
return false;
}
}
There is no need to download all records from the database to your app and then filtering them. With thousands of records it will slow down.
Instead you should create an index on c_mobile field then use just like this simple method:
public interface CustomerRepo extends JpaRepository<CustomersEntity, Long> {
CustomersEntity findByMobileNo(String mobileNo);
}
It will work in a flash (with index).
More info about building query methods you can find here.

Customize update entity on Spring Data repository

I have XRepository interface (extends JpaRepository). On create or update X entity i need to call method of another repository (YRepository) in transaction (exactly: update some field and use new value in created/updated entity X).
To do that i created a service class with #Transactional methods and custom REST Controller. POST mapping on controller works OK and is acceptable for me, but have problem how to implement in more elegant way update (PUT/PATCH) existing entity in my service layer. It works too, but had to use BeanUtils.copyProperties(). Is a better, more conventional way to do that ?
public interface XRepository extends JpaRepository<X, Long> {
}
public interface YRepository extends JpaRepository<Y, Long> {
}
#BasePathAwareController
public class XRestControllerCustom {
#Autowired
private MyService myService;
#PostMapping("/x")
public #ResponseBody ResponseEntity<X> create(#RequestBody Resource<X> x) {
return new ResponseEntity<X>(myService.save(x.getContent()), HttpStatus.CREATED);
}
#PatchMapping("/x/{id}")
public #ResponseBody ResponseEntity<X> update(#PathVariable Long id, #RequestBody Resource<X> x) {
myService.update(id, x.getContent());
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
#Component
public class MyService {
#Autowired
private XRepository xRepository;
#Autowired
private YRepository yRepository;
#Transactional
public X save(X x) {
yRepository.update();
x.setField(yRepository.get());
return xRepository.save(x);
}
#Transactional
public X update(Long id, X partial) {
X x = xRepository.getOne(id);
BeanUtils.copyProperties(x, partial);
x.setId(id); // because copyProperties makes id null
yRepository.update();
x.setField(yRepository.get());
return xRepository.save(x);
}
}

Common spring jpa repository with multiple entity finders

My application has over 250 tables with each having ID and name columns. I am trying to migrate our application from hibernate 3 to Spring-JPA 4.3 with hibernate 5+.
In my current hibernate layer I have (Option 1):
public class DAO
{
private Session session;
public DAO(Session session)
{
this.session=session;
}
public EntityA findById(String id)
{
//implementation
return entityA;
}
public EntityB findByName(String name)
{
//implementation
return entityB;
}
public EntityC findByIdAndName(String id, String name)
{
//implementation
return entityC;
}
}
Back in the days i could have done the following with more generic methods but I didn't want to reinitialize this class if i have 10 different entities to fetch by ID.
public class DAO<T>
{
public T findById(String id)
{
//implementation
return T;
}
public T findByName(String name)
{
//implementation
return T;
}
public T findByIdAndName(String id, String name)
{
//implementation
return T;
}
}
Now how can i achieve this in Spring-JPA. So If i need to get 10 different entities by ID, i don't want to initialize 10 repositories, i want to have one repository that i can use to fetch any entity i want byId or byName or byIDAndName. I could do it easily with JdbcTemplate but that means it may not be tracked by JPA/hibernate caching mechanism.
So how can do the following in one JPA repository:
{
#Query("from EntityA where id=?1")
EntityA findEntityAById(String id);
#Query("from EntityB where name=?1")
EntityB findEntityBById(String name);
#Query("from EntityC where id=?1 and name=?2")
EntityC findEntityCById(String id,String name);
}
You should be able to create a super class(es) that has the common attributes, mark it as a #MappedSuperClass and create a repository for that super class as #NoRepositoryBean. You'll just have to do some casting for your results.
See this answer

Resources