I found so many references about Spring Boot Caching.
I am able to implement at Repository method level.
#Cacheable(value = "booksByCategory", key = "#p0.categoryId", unless = "#result == null")
#EntityGraph(attributePaths = { "category" })
List<Book> findAllByCategoryId(Category category);
I would like cache the data at server startup instead of on first request.
How to do that ?
If you really want to "pre-load" your cache you could call findAllByCategoryId(category) for each expected category in a #PostConstruct method for the class that contains this method; e.g.:
#PostContstruct
void initCache() {
proxyRefToThisBean.findAllByCategoryId(cat1);
proxyRefToThisBean.findAllByCategoryId(cat2);
...
}
A method annotated with PostConstruct will be called during startup when your dependencies are being initialized.
Related
I'm not sure if anyone has experienced this particular twist of the LazyInitialization issue.
I have a console Spring Boot application (so: no views - everything basically happens within a single execution method). I have the standard beans and bean repositories, and the typical lazy relationship in one of the beans, and it turns out that unless I specify #Transactional, any access to any lazy collection automatically fails even though the session stays the same, is available, and is open. In fact, I can happily do any session-based operation as long as I don't try to access a lazy collection.
Here's a more detailed example :
#Entity
class Path {
... `
#OneToMany(mappedBy = "path",fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#OrderBy("projectOrder") `
public List<Project> getProjects() {
return projects; `
}`
}
Now, the main method does something as simple as this:
class SpringTest {
... `
#Autowired
private PathRepository pathRepository;
void foo() {
Path path = pathRepository.findByNameKey("...");
System.out.println(path.getProjects()); // Boom <- Lazy Initialization exception}
}
Of course if I slap a #Transactional on top of the method or class it works, but the point is - why should I need that? No one is closing the session, so why is Hibernate complaining that thereĀ“s no session when there is one?
In fact, If I do:
void foo() {
System.out.println(entityManager.unwrap(Session.class));
System.out.println(entityManager.unwrap(Session.class).isOpen());
Path basic = pathRepository.findByNameKey("...");
System.out.println(entityManager.unwrap(Session.class));
System.out.println(entityManager.unwrap(Session.class).isOpen());
System.out.println(((AbstractPersistentCollection)basic.projects).getSession());
Path p1 = pathRepository.findByNameKey("....");
}
I get that the session object stays the same the whole time, it stays open the whole time, but the internal session property of the collection is never set to anything other than null, so of course when Hibernate tries to read that collection, in its withTemporarySessionIfNeeded method it immediately throws an exception
private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
SharedSessionContractImplementor tempSession = null;
if (this.session == null) {
if (this.allowLoadOutsideTransaction) {
tempSession = this.openTemporarySessionForLoading();
} else {
this.throwLazyInitializationException("could not initialize proxy - no Session");
}
So I guess my question would be - why is this happening? Why doesn't Hibernate store or access the session from which a bean was fetched so that it can load the lazy collection from it?
Digging a bit deeper, it turns out that the repository method executying the query does a
// method here is java.lang.Object org.hibernate.query.Query.getSingleResult()
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
if (SharedEntityManagerCreator.queryTerminatingMethods.contains(method.getName())) {
...
EntityManagerFactoryUtils.closeEntityManager(this.entityManager); // <--- Why?!?!?
this.entityManager = null;
}
...
}
and the above closeEntityManager calls unsetSession on all collections:
SharedSessionContractImplementor session = this.getSession();
if (this.collectionEntries != null) {
IdentityMap.onEachKey(this.collectionEntries, (k) -> {
k.unsetSession(session);
});
}
But why?!
(Spring Boot version is 2.7.8)
So, after researching more it appears that this is standard behavior in Spring Boot - unless you use your own EntityManager, the one managed automatically by Spring is either attached to a #Transactional boundary, or opens and closes for each query.
Some relevant links:
Does Entity manager needs to be closed every query?
Do I have to close() every EntityManager?
In the end, I ended using a TransactionTemplate to wrap my code into a transaction without having to mark the whole class #Transactional.
please can somebody help me?
I have experience with JPA, but not so with Spring, that hides many aspects, that are visible in JPA (for example Hibernate implementation).
Often I was used to work in JPA in this mode (one global transaction) - I will try to explain on saving header (method_A) and its items (method_B) - with result in posting all or nothing. I would like to reach this effect via Spring persistence. I know, that method with #Transactional annotation gets the session from outside, if this exists. My problem is, that I think, that the nested implemented save() method of default Spring repository interface (CrudRepository for example) will open its own transaction anyway - and this is, what I don't want - simply I need to force this save() method to get it from outside. And so I am not sure, if only #Transactional annotation is enough to force this behavior.
This is my JPA code, that works properly in Hibernate:
root_method() {
Transaction tx = session.beginTransaction();
method_A(session);
tx.commit();
}
method_A(Session session) {
Header header = new Header();
session.save(header);
for (Item item : getJustAllocatedItems()) {
item.setHeader(header);
method_B(session, item);
}
}
method_B(Session session, Item item) {
session.save(item);
}
I am sorry, that this is not pure Java, but for explanation purposes I hope it is enough. I will try to mirror Spring code in brute form:
#Transactional
root_method() {
// ... root_method should make overal transaction frame on background (I suppose, that root_method is not called from another method with #Transactional ann.)
method_A();
}
#Transactional
method_A() {
Header header = new Header();
headerRepository.save(header);
for (Item item : getJustAllocatedItems()) {
item.setHeader(header);
method_B(item);
}
}
#Transactional
method_B(Item item) {
itemRepository.save(item);
}
... so I do not think, that save() methods of repositories (in both A and B method) will receive and use transaction from outside - am I right? - and if it is so, please can somebody interpret my JPA code from first part to appropriate Spring representation. Thanks so much.
If you call repository method without transaction, then repository
will create transaction:
Creating new transaction with name [org...SimpleJpaRepository.save]
Committing JPA transaction on EntityManager
If you use transactional annotation (Note that it should be in separate service), then save will reuse transaction:
Creating new transaction with name [com...HeaderService.createHeader]
Committing JPA transaction on EntityManager
Please note, mathods, annotated with #Transactional, should be in different classes (or you should autowire current class using setter). Then Spring will be able to use proxy. Spring wraps service with #Transactional annoaions into proxy.
Enable jpa logging:
logging.level.org.springframework.orm.jpa=DEBUG
logging.level.org.springframework.transaction=DEBUG
This is example implementation of your classes hierarcy:
#Service
#AllArgsConstructor
public class HeaderService {
HeaderRepository headerRepository;
ItemService itemService;
#Transactional
public void methodA() {
Header header = new Header();
headerRepository.save(header);
for (Item item : getJustAllocatedItems()) {
item.setHeader(header);
itemService.methodB(item);
}
}
}
#Service
#AllArgsConstructor
public class ItemService {
ItemRepository itemRepository;
#Transactional
void methodB(item) {
itemRepository.save(item);
}
}
public interface HeaderRepository extends CrudRepository<Header, Long> { }
public interface ItemRepository extends CrudRepository<Item, Long> { }
I am a beginner in spring boot and there is one thing that confuses me. As I understand it, in spring data jpa an EntityManger of type "container-managed transaction-scoped persistence context" is used. So according to my assumption, for each transactional service method a separate EntityManager should always be created to manage the entities within the method and after the method terminates all managed entities should be in detached state.
In the example below, StudentController calls 2 StudentService transactional methods. I thought that "studentService.insertStudent(studentEntity)" returns a detached entity and therefore "studentService.updateStudent(studentEntity1)" has no effect. But on the contrary, the entity is updated in DB, which means that it is still managed.
StudentController.java
#RequestMapping(
value = "/student",
method = {RequestMethod.PUT},
consumes = "application/json",
produces = "application/json"
)
public ResponseEntity<StudentEntity> insertStudent(#RequestBody StudentEntity studentEntity) {
StudentEntity studentEntity1 = studentService.insertStudent(studentEntity);
StudentEntity studentEntity2 = studentService.updateStudent(studentEntity1);
return new ResponseEntity<>(studentEntity2, HttpStatus.OK);
}
StudentService.java
#Transactional
public StudentEntity insertStudent(StudentEntity studentEntity){
return studentRepository.save(studentEntity);
}
#Transactional
public StudentEntity updateStudent(StudentEntity studentEntity){
studentEntity.setFirstName("firstName!!");
return studentEntity;
}
can someone please help me?
I am using spring jpa transactions in my project.One Case includes inserting a data in a synchronized method and when another thread accesses it the data is not updated.My code is given below :
public UpdatedDTO parentMethod(){
private UpdatedDTO updatedDTO = getSomeMethod();
childmethod1(inputVal);
return updatedDTO;
}
#Transactional
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
Now if two threads access at the same time and if first thread completes childmethod2 and childmethod1 and without completing parentMethod() after that if second thread comes to the childMethod1() and checks if data exists,the data is null and is not updated by first thread.I have tried many ways like
#Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
also tried taking off #transactional in the childMethod1() but nothing works out.I know im doing something wrong here , but couldnt figure out where and what exactly i am doing wrong.Can anyone help me out with this
#Transactional is resolved using proxies on spring beans. It means it will have no effect if your method with #Transactional is called from the same class. Take a look at Spring #Transaction method call by the method within the same class, does not work?
The easiest would be moving those methods into separate service.
Typical checklist I follow in cases like these :
If Java based configuration then make sure
#EnableTransactionManagement annocation is present in the class
containing the #Configuration annotation
Make sure the transactionManager bean is created, again this should be mentioned in the configuration class.
Use of #Transactional annocatio over the method which is calling the repository, typically a class in the DAO layer
Adding the #Service annotation for the class which is invoking the methods in the repository
Nice blog which explains the Transaction configuration with JPA in depth --> http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/68954
I'm trying to call a #Cacheable method from within the same class:
#Cacheable(value = "defaultCache", key = "#id")
public Person findPerson(int id) {
return getSession().getPerson(id);
}
public List<Person> findPersons(int[] ids) {
List<Person> list = new ArrayList<Person>();
for (int id : ids) {
list.add(findPerson(id));
}
return list;
}
and hoping that the results from findPersons are cached as well, but the #Cacheable annotation is ignored, and findPerson method got executed everytime.
Am I doing something wrong here, or this is intended?
This is because of the way proxies are created for handling caching, transaction related functionality in Spring. This is a very good reference of how Spring handles it - Transactions, Caching and AOP: understanding proxy usage in Spring
In short, a self call bypasses the dynamic proxy and any cross cutting concern like caching, transaction etc which is part of the dynamic proxies logic is also bypassed.
The fix is to use AspectJ compile time or load time weaving.
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
#Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
#Cacheable(value = "defaultCache", key = "#id")
public Person findPerson(int id) {
return getSession().getPerson(id);
}
public List<Person> findPersons(int[] ids) {
List<Person> list = new ArrayList<Person>();
for (int id : ids) {
list.add(_personDao.findPerson(id));
}
return list;
}
}
For anyone using the Grails Spring Cache plugin, a workaround is described in the documentation. I had this issue on a grails app, but unfortunately the accepted answer seems to be unusable for Grails. The solution is ugly, IMHO, but it works.
The example code demonstrates it well:
class ExampleService {
def grailsApplication
def nonCachedMethod() {
grailsApplication.mainContext.exampleService.cachedMethod()
}
#Cacheable('cachedMethodCache')
def cachedMethod() {
// do some expensive stuff
}
}
Simply replace exampleService.cachedMethod() with your own service and method.