Taking instance of EntityManager Jpa - java

I am using spring and repository level for connection to the database. I am using EntityManagerFactory and the jpa repository. My config:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.project.models");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
#Bean
public PlatformTransactionManager transactionManager(
EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
But now I have a case where I want to use the EntityManager's create method. How can I take an instance of it and also where, when I am using the Jpa repository and have no concrete class for it, that implements the methods?
I am using a repository that extends JpaRepository:
public interface MyRepository extends JpaRepository<Chat, Integer> {
}
and then using it in my service:
#Service
public class MyServiceImpl implements MyService {
private MyRepository myRepository;
public MyServiceImpl(MyRepository myRepository) {
this.myRepository = myRepository;
}
Should I make an abstract class that implements MyRepository so I don't have to implement all JpaRepository's methods and take the instance of the EntityManager there and how can I do it?

Usually you would just inject EntityManager where ever you need it. Like:
#PersistenceContext
private EntityManager em;
and just use method persist(..) like:
em.persist(myChatObject);
so there should not be a need for any special handling.

Related

Spring #Transactional doesn't apply transactional behavior to method

I try to get my hands dirty with Spring + JPA and I read Spring in Action 4th edition. Because of code samples there are incomplete I look for needed parts of code myself in web. But seems I did smth wrong because when I call EntityManager.persist() it doesn't work. No even exception thrown
My code below
DataConfig
#Configuration
#EnableTransactionManagement
public class DataConfig {
#Bean
public DataSource dataSource() {
SingleConnectionDataSource ds = new SingleConnectionDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/dbname");
ds.setSuppressClose(true);
ds.setUsername("user");
ds.setPassword("password");
return ds;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setShowSql(true);
adapter.setGenerateDdl(false);
adapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
return adapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter){
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setDataSource(dataSource);
emfb.setJpaVendorAdapter(jpaVendorAdapter);
emfb.setPackagesToScan("spittr.domain");
return emfb;
}
#Bean
public PlatformTransactionManager transactionManager(DataSource ds){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setDataSource(ds);
return transactionManager;
}
}
Repository
#Repository
public class JpaSpittleRepository implements SpittleRepository{
#PersistenceUnit
private EntityManagerFactory emf;
//other methods...
#Transactional
public void save(Spittle spittle) {
EntityManager em = emf.createEntityManager();
em.persist(spittle);
}
}
So in this configuration save() saves nothing.
Of course if explicitly add a pair of lines of code to that method like:
#Transactional //unnecessary now
public void save(Spittle spittle) {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(spittle);
tx.commit();
}
save() works fine (even without #Transactional)
Also I found that #Transactional does wrap save() method with doBegin() and doCommit() methods of JpaTransactionManager.
Where would you suggest to dig more to find the problem?
That same book offers another way of configuring Repository by replacing injection of EntityManagerFactory with EntityManager:
//#PersistenceUnit
#PersistenceContext
//private EntityManagerFactory emf;
private EntityManager em;
As explained, calling emf.createEntityManager() in each method creates new EntityManager which complicates matters concerning transactions
Now it works, though for me the root cause of problem still unclear

Spring #Autowired EntityManager failure

I am studying Spring JPA integration and come up with strange behavior when trying to autowire EntityManager with #Autowired annotation.
I have a Spring test that has no Spring Boot context support:
#ExtendWith(SpringExtension.class)
class JpaTransactionScopedPersistentContextTests {
#Configuration
#ComponentScan(basePackages = {
"packages"
})
static class TestConfiguration {}
#Autowired
private EntityAService entityAService;
#Test
void testEntityManagerWithoutTransaction () {
EntityA entityA = new EntityA();
entityAService.save(entityA);
}
}
The service is defined as follows:
#Service
public class EntityAService {
// #PersistenceContext - this works
#Autowired // this fails
private EntityManager entityManager;
public EntityA save (EntityA entityA) {
return entityManager.merge(entityA);
}
#Transactional
public EntityA saveInTransaction (EntityA entityA) {
return entityManager.merge(entityA);
}
}
I have the following JPA configuration:
#EnableTransactionManagement
#Configuration
public class ConfigurationA {
#Bean
public DataSource dataSourceWithEmbeddedDatabaseBuilder () throws SQLException {
return new EmbeddedDatabaseBuilder()
.setName("test")
.setType(EmbeddedDatabaseType.H2)
.addScripts("classpath:/schema.sql")
.addScripts("classpath:/data.sql")
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory (DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setPackagesToScan("packages");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager jpaTransactionManager (EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Spring throws an error:
No qualifying bean of type 'javax.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate.
Why?
After hours of debugging a bunch of internal Spring code I have found the reason.
This:
#Autowired
private EntityManager entityManager;
Works only when you have #EnableJpaRepositories annotation.
For example:
#EnableJpaRepositories
#Configuration
public class ConfigurationA {}
That is, autowiring of EntityManager is a feature that is provided by Spring Data JPA module. It is not provided by usual Spring JPA integration.

Spring Transactional doesn't persist an entity using EntityManager but does using Spring Data

Maybe it is kind of too common but still.
I have a small test project where I'm testing all the JPA stuff. Almost everywhere I'm using Spring Data and JPA repositories work just fine. But now I'm trying to make my service to save entities. The service looks something like this:
#Service
public class SomeServiceImpl implements SomeService {
#Autowired
private EntityManagerFactory entityManagerFactory;
public SomeServiceImpl(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
#Override
#Transactional
public SomeEntity save(SomeEntity someEntity) {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.persist(someEntity);
return someEntity;
}
The persistence config looks like this (I'm intentionally copying and pasting the whole config. Maybe it would help you to reproduce the error):
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories
#PropertySource({"classpath:conf/application.properties"})
public class PersistenceConfig {
#Autowired
private Environment environment;
#Bean
public DataSource dataSource() throws SQLException {
PoolDataSourceImpl dataSource = new PoolDataSourceImpl();
dataSource.setConnectionFactoryClassName(environment.getRequiredProperty("db.driverClassName"));
dataSource.setURL(environment.getRequiredProperty("db.url"));
dataSource.setUser(environment.getRequiredProperty("db.username"));
dataSource.setPassword(environment.getRequiredProperty("db.password"));
dataSource.setFastConnectionFailoverEnabled(
Boolean.valueOf(environment.getRequiredProperty("db.fast.connect.failover.enabled")));
dataSource.setValidateConnectionOnBorrow(true);
dataSource.setSQLForValidateConnection("SELECT SYSDATE FROM DUAL");
dataSource.setONSConfiguration(environment.getRequiredProperty("db.ons.config"));
dataSource.setInitialPoolSize(Integer.valueOf(environment.getRequiredProperty("db.initial.pool.size")));
dataSource.setMinPoolSize(Integer.valueOf(environment.getRequiredProperty("db.min.pool.size")));
dataSource.setMaxPoolSize(Integer.valueOf(environment.getRequiredProperty("db.max.pool.size")));
dataSource.setAbandonedConnectionTimeout(0);
dataSource.setInactiveConnectionTimeout(60 * 25);
dataSource.setTimeToLiveConnectionTimeout(0);
dataSource.setMaxConnectionReuseTime(60 * 30L);
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.setProperty("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.setProperty("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.setProperty("hibernate.hbm2ddl.auto", environment.getRequiredProperty("hibernate.hbm2ddl.auto"));
return properties;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(#Autowired DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setPackagesToScan("com.dropbinc.learning.jpa.model");
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
Map jpaProperties = new HashMap();
jpaProperties.put("javax.persistence.schema-generation.database.action", "drop-and-create");
entityManagerFactory.setJpaPropertyMap(jpaProperties);
entityManagerFactory.setJpaProperties(hibernateProperties());
return entityManagerFactory;
}
#Bean
public PlatformTransactionManager transactionManager(#Autowired EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
And one more where I'm planning to configurate the rest of the application (placed in the same package):
#Configuration
#ComponentScan(basePackages = "com.dropbinc.learning.jpa")
public class AppConfig {
}
I've tried to debug Spring but all that I wasn't able to detect a difference between transaction behaviour of JPA repositories and my service. I saw transaction was created and even commited. But in case of JPA repositories it got saved while in my service implementation it did generated ids but an entity didn't appeared in a database.
I'm running all the stuff in tests, autowiring the service by interface:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { AppConfig.class, PersistenceConfig.class })
public class AllTheTests {
#Autowired
SomeService someService;
...
}
Thank you very much for any suggestion!
EDIT Adding entityManager.flush() call generates nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress.

Why is my entitymanager in Playframework 2.5 still null?

I want to use Playframework 2.5 together with Spring and JPA. I found the following template https://github.com/jamesward/play-java-spring where it works perfectly, unfortunately it’s not for Playframework 2.5. So I decided to adapt this template and create my own one for Playframework 2.5. However, my entitymanager in my controller Application is still null. What am I doing wrong? My code looks like the following:
AppConfig.java
package config;
#Configuration
#ComponentScan({"daos","services","controllers","models"})
public class AppConfig {
}
DataConfig.java
package config;
#Configuration
#EnableTransactionManagement
public class DataConfig
{
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPackagesToScan("models");
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
entityManagerFactory.setDataSource(dataSource());
entityManagerFactory.setJpaPropertyMap(new HashMap<String, String>(){{
put("hibernate.hbm2ddl.auto", "create-drop");
put("hibernate.dialect","org.hibernate.dialect.PostgreSQLDialect");
}});
entityManagerFactory.afterPropertiesSet();
return entityManagerFactory.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager(entityManagerFactory());
return transactionManager;
}
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
return dataSource;
}
}
Global.java
package config;
#Singleton
#Configuration
public class Global
{
private ConfigurableApplicationContext context;
#Inject
public Global(ApplicationLifecycle lifecyle)
{
this.context = new AnnotationConfigApplicationContext(AppConfig.class, DataConfig.class);
lifecyle.addStopHook(()-> {
context.close();
return CompletableFuture.completedFuture(null);
});
}
}
Application.java
package controllers;
#Controller
#Transactional
#Component
public class Application extends play.mvc.Controller
{
#PersistenceContext
private EntityManager em;
#Transactional
public Result index()
{
System.out.println("******************* EM " + this.em +" *************************");
return ok(index.render());
}
}
Thank you for your help!

Will controller block while making spring/hibernate call?

This is controller after creating project from activator template for Play and Spring sample.
Controller Code:
#org.springframework.stereotype.Controller
public class Application {
#Autowired
private BarService barService;
public Result addBar() {
Form<Bar> form = Form.form(Bar.class).bindFromRequest();
Bar bar = form.get();
barService.addBar(bar);
return play.mvc.Controller.redirect(controllers.routes.Application.index());
}
}
Bar Service:
#Service
#Transactional
public class BarServiceImpl implements BarService {
#PersistenceContext
EntityManager em;
#Override
public void addBar(Bar bar) {
em.persist(bar);
}
#Override
public List<Bar> getAllBars() {
CriteriaQuery<Bar> c = em.getCriteriaBuilder().createQuery(Bar.class);
c.from(Bar.class);
return em.createQuery(c).getResultList();
}
}
Spring Hibernate configuration:
#Configuration
#EnableTransactionManagement
public class DataConfig {
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPackagesToScan("models");
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
entityManagerFactory.setDataSource(dataSource());
entityManagerFactory.setJpaPropertyMap(new HashMap<String, String>(){{
put("hibernate.hbm2ddl.auto", "create-drop");
}});
entityManagerFactory.afterPropertiesSet();
return entityManagerFactory.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager(entityManagerFactory());
return transactionManager;
}
#Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Play.application().configuration().getString("db.default.driver"));
dataSource.setUrl(Play.application().configuration().getString("db.default.url"));
return dataSource;
}
}
My question is when controller calls addBar function in barService is it a blocking call? If yes then what should be the proper way of doing spring/hibernate integration in a Play application considering it is a sample code from Typesafe activator itself.
Yes, it blocks because JDBC does not have async/non-blocking support. And since Hibernate depends on JDBC, it inherits its blocking behavior. This is also documented here:
Common examples of such blocking operations are JDBC calls, streaming API, HTTP requests and long computations.
I highly recommend that you read the following documentation pages:
JavaAsync: Handling asynchronous results
Understanding Play thread pools
I also recommend that you take a look at other very similar discussions here:
https://stackoverflow.com/a/32784410/4600

Categories

Resources