Spring JPA - cannot update in while loop, insert works - java

I am using spring data/jpa to perform some database operations. I have a while loop which runs and successfully inserts data as it runs, but i also need an update operation to happen at the end of each run of a while loop. Here is basically what I have in a simple example. This is exactly the structure I am using.
Class doing all the operations:
#Component
public class MyClassImpl implements MyClass {
#Autowired
MyOtherClass myOtherClass;
#Override
public void run() {
while (expression) {
// get some data into and entity object
myOtherClass.insertMethod(entity);
myOtherClass.updateMethod(entityId);
}
}
}
my other class:
#Component
public class MyOtherClassImpl implements MyOtherClass {
#Override
JpaClass jpaClass;
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertMethod(EntityObject entity) {
jpaClass.save(entity);
}
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateMethod(String entityId) {
EntityObject entity = jpaClass.findById(entityId);
//change something on the entity
jpaClass.save(entity);
}
}
entity object:
public interface JpaClass extends JpaRepository<EntityObject, Long> {
EntityObject findById(String entityId);
}
the problem I am having is that the insert works just fine, but within the while loop I cannot get any updates to work like i have them. I have tried moving the logic around and putting the findById logic in a different method but cannot get it working. I am trying to update 1 row in a table which handles 1 value I then need to reference in the next run of the while loop.
so it goes:
get value
operate using value
update value
repeat
I set up the database config using spring #Configuration on a class which works fine for all transactions, for reference it is essentially set up like this:
#Configuration
#EnableTransactionManagement
#PropertySource(value = { "classpath:/${app.execution.environment}/application.properties" })
#EnableJpaRepositories(basePackages = "com.example", entityManagerFactoryRef = "mysqlEntityManager", transactionManagerRef = "mysqlTransactionManager")
public class MysqlHibernateConfig {
// all the needed beans here
}
Just to confirm as well, i ran this logic without the while loop and the data does update as expected, so the problem is somewhere in the database transaction, but I am stuck on how to resolve it.

This problem is caused by cache.
You can try like this.
in service class,
#Autowired
private EntityManager entityManager;
entityManager.clear();
in application.properties, you should set
spring.jpa.open-in-view = false

Related

Hibernate LazyInitializationException if entity is fetched in JWTAuthorizationFilter

I'm using Spring Rest. I have an Entity called Operator that goes like this:
#Entity
#Table(name = "operators")
public class Operator {
//various properties
private List<OperatorRole> operatorRoles;
//various getters and setters
#LazyCollection(LazyCollectionOption.TRUE)
#OneToMany(mappedBy = "operator", cascade = CascadeType.ALL)
public List<OperatorRole> getOperatorRoles() {
return operatorRoles;
}
public void setOperatorRoles(List<OperatorRole> operatorRoles) {
this.operatorRoles = operatorRoles;
}
}
I also have the corresponding OperatorRepository extends JpaRepository
I defined a controller that exposes this API:
#RestController
#RequestMapping("/api/operators")
public class OperatorController{
private final OperatorRepository operatorRepository;
#Autowired
public OperatorController(OperatorRepository operatorRepository) {
this.operatorRepository = operatorRepository;
}
#GetMapping(value = "/myApi")
#Transactional(readOnly = true)
public MyResponseBody myApi(#ApiIgnore #AuthorizedConsumer Operator operator){
if(operator.getOperatorRoles()!=null) {
for (OperatorRole current : operator.getOperatorRoles()) {
//do things
}
}
}
}
This used to work before I made the OperatorRoles list lazy; now if I try to iterate through the list it throws LazyInitializationException.
The Operator parameter is fetched from the DB by a filter that extends Spring's BasicAuthenticationFilter, and is then somehow autowired into the API call.
I can get other, non-lazy initialized, properties without problem. If i do something like operator = operatorRepository.getOne(operator.getId());, everything works, but I would need to change this in too many points in the code.
From what I understand, the problem is that the session used to fetch the Operator in the BasicAuthenticationFilter is no longer open by the time i reach the actual API in OperatorController.
I managed to wrap everything in a OpenSessionInViewFilter, but it still doesn't work.
Anyone has any ideas?
I was having this very same problem for a long time and was using FetchType.EAGER but today something has clicked in my head ...
#Transactional didn't work so I thought "if declarative transactions don't work? Maybe programmatically do" And they do!
Based on Spring Programmatic Transactions docs:
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private final TransactionTemplate transactionTemplate;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager,
PlatformTransactionManager transactionManager) {
super(authenticationManager);
this.transactionTemplate = new TransactionTemplate(transactionManager);
// Set your desired propagation behavior, isolation level, readOnly, etc.
this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
}
private void doSomething() {
transactionTemplate.execute(transactionStatus -> {
// execute your queries
});
}
}
It could be late for you, but I hope it helps others.

How to cache data during application startup in Spring boot application

I have a Spring boot Application connecting to SQL Server Database. I need some help in using caching in my application. I have a table for CodeCategory which has a list of codes for Many codes. This table will be loaded every month and data changes only once in a month.
I want to cache this entire table when the Application starts. In any subsequent calls to the table should get value from this cache instead of calling the Database.
For Example,
List<CodeCategory> findAll();
I want to cache the above DB query value during application startup. If there is a DB call like List<CodeCategory> findByCodeValue(String code) should fetch the code result from the already Cached data instead of calling the Database.
Please let me know how this can be achieved using spring boot and ehcache.
As pointed out, It takes some time for ehcache to setup and it is not working completely with #PostConstruct. In that case make use of ApplicationStartedEvent to load the cache.
GitHub Repo: spring-ehcache-demo
#Service
class CodeCategoryService{
#EventListener(classes = ApplicationStartedEvent.class )
public void listenToStart(ApplicationStartedEvent event) {
this.repo.findByCodeValue("100");
}
}
interface CodeCategoryRepository extends JpaRepository<CodeCategory, Long>{
#Cacheable(value = "codeValues")
List<CodeCategory> findByCodeValue(String code);
}
Note: There are multiple ways as pointed by others. You can choose as per your needs.
My way is to define a generic cache handler
#FunctionalInterface
public interface GenericCacheHandler {
List<CodeCategory> findAll();
}
And its implementation as below
#Component
#EnableScheduling // Important
public class GenericCacheHandlerImpl implements GenericCacheHandler {
#Autowired
private CodeRepository codeRepo;
private List<CodeCategory> codes = new ArrayList<>();
#PostConstruct
private void intializeBudgetState() {
List<CodeCategory> codeList = codeRepo.findAll();
// Any customization goes here
codes = codeList;
}
#Override
public List<CodeCategory> getCodes() {
return codes;
}
}
Call it in Service layer as below
#Service
public class CodeServiceImpl implements CodeService {
#Autowired
private GenericCacheHandler genericCacheHandler;
#Override
public CodeDTO anyMethod() {
return genericCacheHandler.getCodes();
}
}
Use the second level hibernate caching to cache all the required db queries.
For caching at the application start-up, we can use #PostContruct in any of the Service class.
Syntax will be :-
#Service
public class anyService{
#PostConstruct
public void init(){
//call any method
}
}
Use CommandLineRunner interface.
Basically , you can create a Spring #Component and implement CommandLineRunner interface. You will have to override it's run method. The run method will be called at the start of the app.
#Component
public class DatabaseLoader implements
CommandLineRunner {
#override
Public void run(.... string){
// Any code here gets called at the start of the app.
}}
This approach is mostly used to bootstrap the application with some initial data.

How to tell Hibernate NOT to store data while running JUnit tests?

I want to check my persistence logic and so I am running some test cases.
Repository class:
#Repository
public class MyRepository {
public void add(Object obj) {
/* Do some stuff here */
getSession().persist(obj);
}
}
Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Context.class})
public class MyTests {
#Inject
private MyRepository myRepository;
#Test
#Rollback(true)
public void foo() {
/* Test logic */
myRepository.add(obj);
Assert.assert...;
}
}
The unit test: MyTests.java contains test cases which test some stuff that is related to persistence, but not the actual Hibernate persistence itself, so that's why the getSession.persist() statement is obsolete.
For performance reasons, I want to prevent Hibernate from storing data to my database, even if the whole data interaction is rolled back. My approach would be to mock: getSession().persist(). Is there a better, or more specifically, an easier way to achieve my intentions?
First of all, there are different id generators in Hibernate. If identity generator (not all the databases supports it), then to assign id to the entity, when session.persist method is called, insert query will be called. But if, for example, sequence or uuid generator is used, then insert won't be triggered (at least right away).
After that if methods session.get or session.load are called to load persisted (in the current session) object, then select query won't be called, because it gets object from Hibernate cache. But if HQL is used to select data, then select query is called. Moreover before it (by default) insert query for persisted object is called too.
This can be changed with FlushMode. By default is set to AUTO. It means:
The Session is sometimes flushed before query execution in order to
ensure that queries never return stale state.
But if getSession().setHibernateFlushMode(FlushMode.MANUAL) is set:
The Session is only ever flushed when Session.flush() is explicitly
called by the application.
Which means insert query won't be called until session.flush is called explicitly. If methods session.get and session.load are further used your code will still work (in the current session). But in case of select HQL query - it won't find the entity since it wasn't persisted. So beware.
Create an interface, implement it using the Hibernate persist() method, and use it in such a way, that:
the normal calls go through the implementation
the test calls go through a mock version of it
public interface MyRepository {
public void add(Object obj);
}
public class MyRepositoryImpl implements MyRepository {
public void add(Object obj) {
getSession().persist(obj);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Context.class})
public class MyTests {
#Mock // we inject the mock instead of the true implementation
private MyRepository myRepository;
#Test
#Rollback(true)
public void foo() {
/* Test logic */
myRepository.add(obj); // the test uses the mocked version
Assert.assert...;
}
}
There are many Java libraries that let you mock objects, e.g.
Mockito
JMock
EasyMock
You need to be able to mock your repository object so that you can use the mock in tests, and use the real one in the rest of your application.
DAO:
#Repository(value="MockRepo")
public class MockMyRepositoryImpl implments MyRepository {
#Override
public void add(Foo foo) {
//Do nothing here
}
}
Test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { Context.class})
public class MyTests {
#Autowired
#Qualifier("MockRepo");
private MyRepository repo;
#Test
public void testFooSave() {
repo.add(obj);
}
}
The alternative is to use a mocking framework as detailed in another answer. Mocking frameworks are more flexible, but if you want something simple that's just going to work then try the above.

Java: How to handle multiple Hibernate transactions in one request?

I'm not sure where to open my Transaction object. Inside the service layer? Or the controller layer?
My Controller basically has two services, let's call them AService and BService. Then my code goes something like:
public class Controller {
public AService aService = new AService();
public BService bService = new BService();
public void doSomething(SomeData data) {
//Transaction transaction = HibernateUtil.getSession().openTransaction();
if (data.getSomeCondition()) {
aService.save(data.getSomeVar1());
bService.save(data.getSomeVar2());
}
else {
bService.save(data.getSomeVar2());
}
//transaction.commit(); or optional try-catch with rollback
}
}
The behavior I want is that if bService#save fails, then I could invoke a transaction#rollback so that whatever was saved in aService would be rolled back as well. This only seems possible if I create one single transaction for both saves.
But looking at it in a different perspective, it looks really ugly that my Controller is dependent on the Transaction. It would be better if I create the Transaction inside the respective services, (something like how Spring #Transactional works), but if I do it that way, then I don't know how to achieve what I want to happen...
EDIT: Fixed code, added another condition. I am not using any Spring dependencies so the usage of #Transactional is out of the question.
You can accomplish what you're asking with another layer of abstraction and using composition.
public class CompositeABService {
#Autowired
private AService aservice;
#Autowired
private BService bservice;
#Transactional
public void save(Object value1, Object value2) {
aservice.save( value1 );
bservice.save( value2 );
}
}
public class AService {
#Transactional
public void save(Object value) {
// joins an existing transaction if one exists, creates a new one otherwise.
}
}
public class BService {
#Transactional
public void save(Object value) {
// joins an existing transaction if one exists, creates a new one otherwise.
}
}
This same pattern is typically used when you need to interact with multiple repositories as a part of a single unit of work (e.g. transaction).
Now all your controller needs to depend upon is CompositeABService or whatever you wish to name it.

Duplicate object creation with nested transactions using spring,hibernate and mysql

I have two services, like this (simplified code):
#Service
public class OuterService {
#Autowired
InnerService innerService;
#Transactional
public void doSomething() {
List<SomeEntity> list = entityRepo.findByWhatever(...);
for(SomeEntity listElement : list) {
innerService.processEntity(listElement);
}
}
}
#Service
public class InnerService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void processEntity(Entity entity) {
// ...
StatusElement status = new StatusElement(...);
statusElementRepo.save(status);
}
}
The constructed StatusElement is now inserted by exiting InnerService.processEntity() and inserted again by exiting OuterService.doSomething().
If I change the #Transactional annotation of OuterService.doSomething() to #Transactional(readOnly = true), it is inserted just once.
Is it a problem with MySql (because it may not support nested transactions), do I need a special transaction manager, or is there something wrong with my code? TIA!
I solved it by using programmatically transactions using the PlatformTransactionManager.
see: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-programmatic-ptm

Categories