I'm working on a homework that using JPA but without spring.
It has service layer and repository layer.
I try to begin transaction in service and call save or update from repository and then commit in service.
But how to get Current EntityManager in repository?
My cod is like this:
Service:
public void save(Entity entity){
var em = factory.createEntityManager();
var t = em.getTransaction();
try {
t.begin();
repository1.save(entity);
// For saving one to many relation
repository2.save(entity.getChildEntity());
t.commit();
} catch (Exception e) {
t.rollback();
}
}
Repository:
// I don't want to pass EntityManager to method
public void save(T entity) {
var em = ? // How can I get EntityManager hear?
em.persist(entity);
}
In your first snippet of the service I see that you have a factory and there you create an EntityManager. So the factory exists...
Take your factory and make it accessible everywhere in your program. How?
Create a class specific only to creating the factory and the hibernate session. There you create public static getters for the fields that you need (such as the factory and EntityManager).
Example:
public class HibernateUtils
{
public static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory()
{
try
{
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex)
{
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
In Spring, the use of Services and Repositories is a further abstraction of JPA.
If you want to use JPA without Spring, you can inject EntityManager using standard JavaEE/JakartaEE technologies, that is what Spring does under the hood.
Otherwise, if you want to do it by yourself, keep in mind that JPA is a standard for which there are a couple of implementations.
Let's take in consideration Hibernate, go to read the docs, stick with EntityManagerFactory and EntityManager instead of SessionManager (specific to Hibernate).
You can instantiate an EntityManager as:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("my_PU");
EntityManager em = emf.createEntityManager();
The "my_PU" is a persistenceUnit that you need to define.
More info here: https://docs.oracle.com/cd/E19798-01/821-1841/bnbrj/index.html#:~:text=A%20persistence%20unit%20defines%20a,the%20persistence.xml%20configuration%20file.
public class EntityManagerProvider {
private static final EntityManager entityManager;
public EntityManagerProvider() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.example.factory");
this.entityManager = emf.createEntityManager();
}
public static EntityManager getEntityManager() {
return entityManager;
}
}
Second Approach :
public class EntityManagerProvider {
private static final EntityManager entityManager;
private EntityManagerProvider() {}
public static synchronized EntityManager createOrGetEntityManager() {
if(entityManager == null) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.example.factory");
EntityManagerProvider.entityManager = emf.createEntityManager();
return EntityManagerProvider.entityManager;
}
return EntityManagerProvider.entityManager;
}
}
You need to create entityManager once and use it everywhere. I don't know where you create factory class but you can create entityManagerProvider class after creation of factory class.
You can simply call EntityManagerProvider.getEntityManager() to receive entityManager from class.
Please note that you don't need to initialize everywhere you can do it once and use it everywhere.
SecondApproach is better you can use it simply everywhere. EntityManagerProvider.createOrGetEntityManager method will give you entityManager.
After some time I found a safe way to get Current EntityManager in current thread.
I lust link so just sharing code that I want get your feedback and opinions.
I think in this way I can even user Aspectj too.
public class EntityManagerUtil {
//for holding EntityManager in current thread
private static final ThreadLocal<EntityManager> THREAD_LOCAL;
static {
THREAD_LOCAL = new ThreadLocal<>();
}
// singleton creating EntityManagerFactory
private static class LazyHolder {
private static final EntityManagerFactory ENTITY_MANAGER_FACTORY =
Persistence.createEntityManagerFactory("persistence-unit");
}
public static EntityManagerFactory getEntityManagerFactory() {
return LazyHolder.ENTITY_MANAGER_FACTORY;
}
public static EntityManager getCurrentEntityManager() {
EntityManager entityManager = THREAD_LOCAL.get();
if (entityManager == null) {
entityManager = getEntityManagerFactory().createEntityManager();
// set your flush mode here
THREAD_LOCAL.set(entityManager);
}
return entityManager;
}
public static void closeCurrentEntityManager() {
EntityManager em = THREAD_LOCAL.get();
if (em != null) {
em.close();
THREAD_LOCAL.remove();
}
}
}
Related
I am trying to use the #Async capabilities of the Spring framework to perform a simple indexing task.
The problem I'm facing is that I feel that the EntityManager used in my Async function is somehow reused from previous calls so my data is not up to date and sometimes uses old data.
Here is the code I wrote as an example. The goal is to update a product's data and index it asynchronously after I publish an event using Spring's ApplicationEventPublisher:
ProductService
#Service
class ProductService {
private final EntityManager entityManager;
private final ApplicationEventPublisher eventPublisher;
#Autowired
public ProductService(EntityManager entityManager, ApplicationEventPublisher eventPublisher) {
this.entityManager = entityManager;
this.eventPublisher = eventPublisher;
}
#Transactional
public void patchProduct (String id, ProductDto productDto) {
Product product = this.entityManager.find(Product.class, id);
product.setLabel(productDto.getLabel());
this.entityManager.flush();
this.eventPublisher.publishEvent(new ProductEvent(product, ProductEvent.EVENT_TYPE.UPDATED));
}
}
EventListener
#Component
public class ProductEventListener {
private final AsyncProcesses asyncProcesses;
#Autowired
public ProductEventListener (
AsyncProcesses asyncProcesses
) {
this.asyncProcesses = asyncProcesses;
}
#EventListener
public void indexProduct (ProductEvent productEvent) {
this.asyncProcesses.indexProduct(productEvent.getProduct().getPok());
}
}
AsyncProcesses
#Service
public class AsyncProcesses {
private final SlowProcesses slowProcesses;
#Autowired
public AsyncProcesses(SlowProcesses slowProcesses) {
this.slowProcesses = slowProcesses;
}
#Async
public void indexProduct (String id) {
this.slowProcesses.indexProduct(id);
}
}
SlowProcesses
#Service
public class SlowProcesses {
private EntityManager entityManager;
private ProductSearchService productSearchService;
#Autowired
public SlowProcesses(EntityManager entityManager, NewProductSearchService newProductSearchService) {
this.entityManager = entityManager;
this.newProductSearchService = newProductSearchService;
}
#Transactional(readonly = true)
public void indexProduct (String pok) {
Product product = this.entityManager.find(Product.class, pok);
// this.entityManager.refresh(product); -> If I uncomment this line, everything works as expected
this.productSearchService.indexProduct(product);
}
}
As you can see on the SlowProcesses file, if I refresh the product object in the entityManager, I get the correct and up to date data. If I do not, I might get old data from previous calls.
What is the correct way to use the EntityManager in an Asynchronous call? Do I really have to refresh all my objects in order to make everything work? Am I doing something else wrong?
Thank you for reading through
Since instances of EntityManager are not thread-safe as pointed out by Jordie, you may want to try this instead:
Instead of injecting an EntityManager, inject an EntityManagerFactory. Then from the EntityManagerFactory retrieve a new EntityManager instance that is used only for the duration of the method in question.
I am using JPA 2.1. Entity Managers are application managed here.
Sample Class:
public class TestDao {
private static EntityManagerFactory emf;
private static EntityManager em;
public static EntityManager initialize() {
if (emf == null) {
emf = Persistence.createEntityManagerFactory("persistence_unit");
}
return emf.createEntityManager();
}
public static void insert(Object obj){
em.persist(obj);
}
When user first time uses TestDao.initialize(), it generates emf and em instances.
What will happen with this emf instance?
Does it always keep connection with the database?
Which is better way if I have both significant reads and writes:
create emf once (as I used above)
create new emf and em every time I interact with the database.
As per the Factory Pattern, it should be created/initialized only once. So EntityManagerFactory should be only one through out the application. You can create EntityManager from this factory as and when required.
References: Factory Pattern,
When should be factory instance be created?
I am learning to use JPA. And I'm a little confused.
According JPA EntityManager manages transactions. But a design pattern is to inject the EntityManager in DAOs. So how is possible that are different EntityManager to the same transaction?
This is the case I want to solve
I have the DAOs defined
#Repository
JPARepository1 {
#PersistenceContext
protected EntityManager em;
....
.
#Repository
JPARepository2 {
#PersistenceContext
protected EntityManager em;
....
I have a Service
#Service
public class ServiceImpl1 {
#Autowired
private JPARepository1 repo1;
#Autowired
private JPARepository2 repo2;
public void mainMethod(){
Object o= transactionalMethod1();
try{
transactionalMethod2(o);
}catch (Exception e){
transactionalMethod3(o);
}
}
private Object transactionalMethod1(){
....
}
private void transactionalMethod2(Object o){
....
}
private void transactionalMethod3(Object o){
....
}
Then from #Controller I will invoke mainMethod().
What would be the right way to do transactional to transactionalMethod1, transactionalMethod2 and transactionalMethod3,within the same Service and using the same Repository's.
I would like it if there is an exeption in transactionalMethod2, this abort the transaction, but keep the transactions of transactionalMethod1 and transactionalMethod3
Thanks, sorry for my English
Usually you configure one EntityManager, so the wired manager is always the same, the one you configured. The instance of this manager though, is different in every wiring.
So, every transaction in your service uses a different instance of the EntityManager and thus every transaction invoked is seperated from each other.
As so, an exception in transactionalMethod2 doesn't necessarily affects the transactionalMethod1 and transactionalMethod3
What would be the right way to do transactional to transactionalMethod1, transactionalMethod2 and transactionalMethod3,within the same Service and using the same Repository's.
Now, you have two options to do service methods transactions
1) You could annotate your whole #Service like that:
#Service
#Transactional
public class ServiceImpl1 {
....
so every method declared here is also a transaction.
2) You could annotate each method as #Transactional:
#Transactional
private Object transactionalMethod1(){
....
}
#Transactional
private void transactionalMethod2(Object o){
....
}
#Transactional
private void transactionalMethod3(Object o){
....
}
If you want to use a single repository just #Autowired a single one and use it in your #Transactional method. E.g:
#Service
#Transactional
public class ServiceImpl1 {
#Autowired
private JPARepository1 repo1;
public void mainMethod(){
Object o= transactionalMethod1();
try{
transactionalMethod2(o);
}catch (Exception e){
transactionalMethod3(o);
}
}
private Object transactionalMethod1(){
return repo1.findOne();
}
private void transactionalMethod2(Object o){
repo1.create(o);
}
private void transactionalMethod3(Object o){
repo1.delete(o)
}
I would like to know which way is the best to define entity manager. I am using spring boot
case 1) creating in spring service class like follows
#Service
#Transactional
public class DemoService {
private static final Logger log = LoggerFactory.getLogger(DemoService.class);
private EntityManagerFactory emf;
public void getEntity(){
final EntityManager em = emf.createEntityManager();
}
#PersistenceUnit
public void setEntityManagerFactory(final EntityManagerFactory emf) {
this.emf = emf;
}
}
Case 2.) Define a global entity manager and share it across all services.
Note : Each service only reflects one single Entity definition.
Injecting the EntityManager is the simplest and the most effective way to do it:
#PersistenceContext(unitName = "persistenceUnit")
private EntityManager entityManager;
You don't need to set the EntityManagerFactory, since you need a transaction-bound EntityManager.
You don't need to hold the EntityManager in a global component, since that would be yet another indirection layer and you can simply mock the EntityManager anyway.
I am having a bean within which I create a new Thread with Runnable:
#Component
public class MyBean {
private final Task task = new Task();
#PersistenceContext
EntityManager em;
#PostConstruct
public void init() {
task.setEntityManager(em);
new Thread(task).start();
}
public static class Task implements Runnable {
#Setter
private EntityManager em;
#Override
public void run() {
while (true) {
// working with EntityManager
Thing t = em.findById(...); // Fetching a Thing from repo
t.getSomethingList(); // LazyInit exception
wait();
}
}
}
}
Withing the init method, new Thread is created with instance of EntityManager. When I try to load something from the repository the session is instantly closed and getting any lazy field results in failed to lazily initialize a collection of role: Something, no session or session was closed exception from Hibernate.
I tried all the #Transactional annotations with no effect. I need to achieve something like OpenEntityManagerInView, but with the difference that this is not within view.
Thanks
EDIT1:
According to comments - I tried using em.getTransaction().begin(); but this is getting me Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT.
skirsch suggested that I should invoke Transactional method on some other bean. That is what I actually do - exactly as you suggested. I wanted to make things simpler and I did not realize the difference, so I demostrated the problem directly in the class Task. So to summarize, I have it exactly like skirsch suggested, but the problem persists.
As Spring is not managing your Runnable, annotating it won't have the desired effect. So you either need to use an annotated (and Spring-managed) bean from within your Runnable or you need to take care of the txn management manually.
Use Spring transaction management
You define some kind of service
#Service
public class MyService {
#PersistenceContext
EntityManager em;
#Transactional
public void doSomething() {
Thing t = em.findById(...);
t.getSomethingList();
}
}
And then your bean would look like this:
#Component
public class MyBean {
private final Task task = new Task();
#Autowired
MyService service;
#PostConstruct
public void init() {
task.setService(service);
new Thread(task).start();
}
public static class Task implements Runnable {
#Setter
private MyService service;
#Override
public void run() {
while (true) {
service.doSomething();
wait();
}
}
}
}
Manual transaction management
In case you set up JPA Resource Local Transactions, here you go:
// working with EntityManager
em.getTransaction().begin()
try {
Thing t = em.findById(...);
t.getSomethingList();
} finally {
em.getTransaction().rollback()
}
wait();