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
Related
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.
I'm writing a RESTful web service using Spring MVC, using Java Configuration. My configuration file is below. My issue is this -- I discovered that 2 instances of "myService" bean is being created, instead of just one instance. I'm not sure why? How can I adjust the configuration to create only one?
Can anyone point me in the right direction? Thanks!
Here's my configuration class....
#Configuration
public class MyConfig {
#Bean(name = "dataSource")
public DriverManagerDataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// datasource set up
return dataSource;
}
#Autowired
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DriverManagerDataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
sessionBuilder.scanPackages("com.mypackages");
sessionBuilder.addProperties(getHibernateProperties());
return sessionBuilder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.enable_lazy_load_no_trans", "true");
properties.put("hibernate.id.new_generator_mappings", "true");
return properties;
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
#Bean
public MyMainBean MyMainBean() {
MyMainBean bean = new MyMainBean();
bean.setService(myService());
bean.setValidator(myValidator());
return bean;
}
#Bean(name = "myService")
public MyService myService() {
MyService s = new MyService();
s.setDao1(myDao1());
s.setDao2(myDao2());
s.setCopyUtil(copyUtil());
return s;
}
#Bean
public MyDao1 myDao1() {
return new MyDao1();
}
#Bean
public MyDao2 myDao2() {
return new MyDao2();
}
#Bean
public CopyUtil copyUtil() {
return new CopyUtil();
}
#Bean
public ReportValidator reportValidator() {
ReportValidator validator = new ReportValidator();
validator.setService(myService());
return validator;
}
#Bean
public XMLValidator xmlValidator() {
XMLValidator validator = new XMLValidator();
validator.setService(myService());
return validator;
}
}
Actually, Spring is smart when wiring beans and should only call the myService() function once, and then pass the result to the other myService() calls, resulting in only one bean of MyService.
Make sure you really are getting 2 instances of MyService, e.g. by adding a log in the constructor of the MyService class.
If you truly see more than one constructor log statement, make sure that you are not declaring other MyService beans in other #Configuration classes, or that you are not using any component annotation on the MyService class (i.e. don't use #Service, #Component, #Repository).
If you declare the class with #Service, it effectively instantiates the class and adds it to the context. When you declare it again with #Bean you end up with 2 instances, so don't mix them.
Also, you don't need to use those #Autowired annotations here, or even calls to other beans, because the following will also work:
#Configuration
public class DbConfiguration {
#Bean
public MovieDao dao() {
return new MovieDao();
}
#Bean
public MovieService service(MovieDao dao) {
return new MovieService(dao);
}
}
Spring will see that you need a MovieDao to build a MovieService and it will instantiate the dao first and pass it to the service bean. You don't even need to add #Service or similar annotations to your classes!
It really is that good, hope these tips help ;)
I'm writing application which connects with Oracle Database. I call function from DB which inserts new records to table. And after this callback I can decide what I want to do: commit or rollback.
Unfortunalety I'm new in Spring, so I have problems with configuration. And what's more I want to make this configuration in Java class, not in XML. And here I need your help.
UPDATED CODE:
ApplicationConfig code:
#Configuration
#EnableTransactionManagement
#ComponentScan("hr")
#PropertySource({"classpath:jdbc.properties", "classpath:functions.properties", "classpath:procedures.properties"})
public class ApplicationConfig {
#Autowired
private Environment env;
#Bean(name="dataSource")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driver"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
dataSource.setDefaultAutoCommit(false);
return dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
return jdbcTemplate;
}
#Bean(name="txName")
public PlatformTransactionManager txManager() {
DataSourceTransactionManager txManager = new DataSourceTransactionManager();
txManager.setDataSource(dataSource());
return txManager;
}
}
I have Dao and Service, where both implements proper interface.
Service implementation:
#Service
public class HumanResourcesServiceImpl implements HumanResourcesService {
#Autowired
private HumanResourcesDao hrDao;
#Override
public String generateData(int rowsNumber) {
return hrDao.generateData(rowsNumber);
}
#Override
#Transactional("txName")
public void shouldCommit(boolean doCommit, Connection connection) throws SQLException {
hrDao.shouldCommit(doCommit, connection);
}
}
Dao implementation:
#Repository
public class HumanResourcesDaoImpl implements HumanResourcesDao {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcCall generateData;
#Autowired
public HumanResourcesDaoImpl(JdbcTemplate jdbcTemplate, Environment env) {
this.jdbcTemplate = jdbcTemplate;
generateData = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName(env.getProperty("procedure.generateData"));
}
#Override
public String generateData(int rowsNumber) {
HashMap<String, Object> params = new HashMap<>();
params.put("i_rowsNumber", rowsNumber);
Map<String, Object> m = generateData.execute(params);
return (String) m.get("o_execution_time");
}
#Override
#Transactional("txName")
public void shouldCommit(boolean doCommit, Connection connection) throws SQLException {
if(doCommit) {
connection.commit();
} else {
connection.rollback();
}
}
}
Main class code:
public class Main extends Application implements Initializable {
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
hrService = context.getBean(HumanResourcesService.class);
BasicDataSource ds = (BasicDataSource)context.getBean("dataSource");
Connection connection = ds.getConnection();
//do something and call
//hrService.generateData
//do something and call
//hrService.shouldCommit(true, connection);
//which commit or rollback generated data from previoues callback
}
}
UPDATE:
I think that the problem is with connection, because this statement:
this.jdbcTemplate.getDataSource().getConnection();
creates new connection, so then there is nothing to commit or rollback. But still I can't figure why this doesn't work properly. No errors, no new records...
What is wierd, is that when I debuged connection.commit(); I found out that in DelegatingConnection.java, parameter this has proper connection, but there is something like:
_conn.commit();
and _conn has different connection. Why?
Should I in some way synchronize connection for those 2 methods or what? Or this is only one connection? To be honest, I'm not sure how it works exactly. One connection and all callbacks to stored procedures are in this connection or maybe with each callback new connection is created?
Real question is how to commit or rollback data from previous callback which do insert into table?
One easy way to do this is to annotate the method with #Transactional:
#Transactional
public void myBeanMethod() {
...
if (!doCommit)
throw new IllegalStateException(); // any unchecked will do
}
and spring will roll all database changes back.
Remember to add #EnableTransactionManagement to your spring application/main class
You can use #Transactional and #EnableTransactionManagement to setup transactions without using the XML configuration. In short, annotate the methods/classes you want to have transactions with #Transactional. To setup the transactional management you use the #EnableTransactionManagement inside your #Configuration.
See Springs docs for example on how to use both. The #EnableTransactionManagement is detailed in the JavaDocs but should match the XML configuration.
UPDATE
The problem is that you are mixing raw JDBC calls (java.sql.Connection) with Spring JDBC. When you execute your SimpleJdbcCall, Spring creates a new Connection. This is not the same Connection as the one you later try to commit. Hence, nothing happens when you perform the commit. I tried to somehow get the connection that the SimpleJdbcCall uses, but could not find any easy way.
To test this I tried the following (I did not use params):
#Override
public String generateData(int rowsNumber) {
//HashMap<String, Object> params = new HashMap<>();
//params.put("i_rowsNumber", rowsNumber);
//Map<String, Object> m = generateData.execute(params);
Connection targetConnection = DataSourceUtils.getTargetConnection(generateData.getJdbcTemplate().getDataSource().getConnection());
System.out.println(targetConnection.prepareCall((generateData.getCallString())).execute());
targetConnection.commit();
return (String) m.get("o_execution_time");
}
If I don't save the targetConnection, and instead try to get the connection again by calling DataSourceUtils.getTargetConnection() when committing, nothing happens. Thus, you must commit on the same connection that you perform the statement on. This does not seem to be easy, nor the proper way.
The solution is to drop the java.sql.Connection.commit() call. Instead, you use Spring Transactions completly. If you use #Transaction on the method that performs database call, Spring will automatically commit when the method has finished. If the method body experiences any Exception (even outside the actual database call) it will automatically rollback. In other words, this should suffice for normal Transaction management.
However, if you are doing batch processing, and wish to have more control over your transactions with commits and rollbacks, you can still use Spring. To programatically control transactions with Spring, you can use TransactionTemplate which have commit and rollback methods. Don't have time to give you proper samples, but may do so in later days if you are still stuck ;)
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages="org.saat")
#PropertySource(value="classpath:resources/db.properties",ignoreResourceNotFound=true)
public class AppConfig {
#Autowired
private Environment env;
#Bean(name="dataSource")
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
dataSource.setUsername(env.getProperty("db.username"));
dataSource.setPassword(env.getProperty("db.password"));
return dataSource;
}
#Bean(name="entityManagerFactoryBean")
public LocalContainerEntityManagerFactoryBean getSessionFactory() {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean ();
factoryBean.setDataSource(getDataSource());
factoryBean.setPackagesToScan("org.saat");
factoryBean.setJpaVendorAdapter(getJpaVendorAdapter());
Properties props=new Properties();
props.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
props.put("hibernate.hbm2ddl.auto",env.getProperty("hibernate.hbm2ddl.auto"));
props.put("hibernate.show_sql",env.getProperty("hibernate.show_sql"));
factoryBean.setJpaProperties(props);
return factoryBean;
}
#Bean(name="transactionManager")
public JpaTransactionManager getTransactionManager() {
JpaTransactionManager jpatransactionManager = new JpaTransactionManager();
jpatransactionManager.setEntityManagerFactory(getSessionFactory().getObject());
return jpatransactionManager;
}
#Bean
public JpaVendorAdapter getJpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
}
I'm pretty sure transactions are not being applied in my Spring Boot application, even though I am using #EnableTransactionManagement(mode=AdviceMode.ASPECTJ) & have spring-aspects in my app.
Configuration
#Configuration
public class DatastoreConfig{
#Bean
public DataSource dataSource(){ ///... }
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
#Bean
public String databaseSchema(){
return "mySchema";
}
#Bean
public OpenSessionInViewInterceptor openSessionInViewInterceptor(SessionFactory sessionFactory){
OpenSessionInViewInterceptor result = new OpenSessionInViewInterceptor();
result.setSessionFactory(sessionFactory);
return result;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter vendorAdapter){
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan( Main.class.getPackage().getName() + ".model");
factory.setDataSource(dataSource);
factory.setJpaProperties(getJpaProperties());
factory.afterPropertiesSet();
return factory;
}
#Bean
public HibernateJpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setDatabasePlatform(PostgreSQL9Dialect.class.getName());
return vendorAdapter;
}
#Bean
public SessionFactory sessionFactory(HibernateEntityManagerFactory emf){
SessionFactoryImpl sf = (SessionFactoryImpl) emf.getSessionFactory();
return sf;
}
private Properties getJpaProperties() {
Properties props = new Properties();
// props.put("hibernate.hbm2ddl.auto", "validate");
props.put("hibernate.ejb.naming_strategy", ImprovedNamingStrategy.class.getName());
props.put("hibernate.enable_lazy_load_no_trans", "true");
props.put("jadira.usertype.autoRegisterUserTypes", "true");
return props;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) throws PropertyVetoException {
JpaTransactionManager transactionManager = new JpaTransactionManager(emf);
transactionManager.setRollbackOnCommitFailure(true);
transactionManager.setDataSource(dataSource());
return transactionManager;
}
#Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
Main class
#Configuration
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#ComponentScan
#EnableAsync
#EnableScheduling
#EnableEntityLinks
#EnableAspectJAutoProxy
#EnableApiResources(apiPrefix = "")
#EnableJpaRepositories(transactionManagerRef = "transactionManager")
#EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
#EnableConfigurationProperties
#EnableAutoConfiguration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableWebSocketMessageBroker
#Slf4j
public class Main extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder application) {
return application.sources(Main.class);
}
public static void main(String[] args) {
Object[] sources = {
Main.class,
TomcatConfig.class // include for embedded tomcat...
};
ConfigurableApplicationContext result = SpringApplication.run(sources, args);
}
}
Attempt to verify
One of the ways I was trying to verify was by running
#Component
public class SomeService{
#Transactional
#Override
protected void tryItOut(){
// this is always false
boolean declarativeTransaction = TransactionSynchronizationManager.isActualTransactionActive();
// this throws an exception
TransactionStatus aspect = TransactionAspectSupport.currentTransactionStatus();
}
}
However, if I insert a call to transactionManager.getTransaction(new DefaultTransactionDefinition()); beforehand, declarativeTransaction is true.
Any advice on how to set up #Transactions / verify they're working?
Your configuration is contradicting itself.
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
Means you are using loadtime or compile time weaving of the aspects. Judging on your configuration you aren't using that. If I have to judge your configuration you are just using basic proxies no weaving.Just remove AdviceMode.ASPECTJ and it will work, you also don't need the #EnableAspectJAutoProxy as applying transactions has nothing to do with that it (it can work without it).
Also your configuration is doing a lot to basically not use Spring Boot. You have configured a lot of things that spring boot already does for you automatically, manually.
Spring Boot already configures the following for you (just by only specifying #EnableAutoConfiguration.
EntityManagerFactory
DataSource
JdbcTemplate
Transaction Setup
OpenEntityManagerInView
Spring Web Sockets
Hypermedia support
Spring Data JPA setup
You can remove a whole lot of your configuration and put some properties in application.properties.
If you really need the SessionFactory instead of doing the casting yourself I strongly suggest the use of the HibernateJpaSessionFactoryBean.
I would suspect the following main class to still work (with transaction enabled).
#Configuration
#ComponentScan
#EnableAsync
#EnableScheduling
#EnableAutoConfiguration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
#EnableWebSocketMessageBroker
#Slf4j
public class Main extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder application) {
return application.sources(Main.class);
}
public static void main(String[] args) {
Object[] sources = {
Main.class,
TomcatConfig.class // include for embedded tomcat...
};
ConfigurableApplicationContext result = SpringApplication.run(sources, args);
}
}
With this DatastoreConfig class
#Configuration
public class DatastoreConfig{
#Bean
public FactoryBean<SessionFactory> sessionFactory(EntityManagerFactory emf){
HibernateJpaSessionFactoryBean jpaSessionFactoryBean = new HibernateJpaSessionFactoryBean();
jpaSessionFactoryBean.setEntityManagerFactory(emf);
return jpaSessionFactoryBean;
}
}
And this application.properties
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.jpa.properties.jadira.usertype.autoRegisterUserTypes=true
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
You probably need to add some spring.datasource. properties to setup your datasource correctly. Also judging from the fact that you had a OpenSessionInViewInterceptor you used that instead of the OpenEntityManagerInViewInterceptor (or filter). So you might even be able to remove that.
Not sure if that is al your configuration but you might be able to remove/trim down even more. I suggest a look at the Spring Boot reference guide to get a feeling for what is already automatically configured.
I want to use Spring Data JPA to do the ORM. I have the following declared repository interface:
public interface SegmentRepository extends JpaRepository<Segment, Integer> {
// query methods ...
}
Following is the Java Config class:
#Configuration
#EnableJpaRepositories("com.example.cap.repositories")
#EnableTransactionManagement
public class CAPRepositoryConfig {
#Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(org.postgresql.Driver.class.getName());
ds.setUsername("postgres");
ds.setPassword("password");
ds.setUrl("jdbc:postgresql://localhost:5432/postgres");
ds.setInitialSize(10);
return ds;
}
#Bean
public EntityManagerFactory entityManagerFactory() {
EclipseLinkJpaVendorAdapter vendorAdapter = new EclipseLinkJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
Map<String, Object> jpaProperties = new HashMap<String, Object>();
jpaProperties.put("eclipselink.weaving", "false");
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.example.cap.repositories");
factory.setDataSource(dataSource());
factory.setJpaPropertyMap(jpaProperties);
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
}
And the Segment class is defined in com.example.cap.repositories as:
#Entity
public class Segment {
#Id
private int segmentID;
private int caseID;
private Timestamp segStartTime;
private Timestamp segEndTime;
//setter and getters
}
But when I run the JUnit test using auto injected bean SegmentRepository, I got null point exception for the bean repository:
#ContextConfiguration(classes=CAPRepositoryConfig.class)
public class CAPRepositoryTest {
#Autowired
private SegmentRepository repository;
#Test
public void testRepository() {
Segment seg = repository.findOne(123); //null pointer exception for repository
}
}
According to the Spring Data JPA documentation, the SegmentRepository bean repository should be auto injected as long as I specify #EnableJpaRepositories in the Java Config class. But why do I get null pointer exception for repository in the JUnit test class? Since SegmentRepository is an interface rather than a class, I cannot create the instance through Java Config class.
I think you forget SpringJUnit4ClassRunner which makes #Autowired in tests work:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=CAPRepositoryConfig.class)
public class CAPRepositoryTest { ... }
When I work with test classes and I need to do an unit test, I prefer instantiate the class because although you have an interface you need to have to an implementation class too. In my case I do something like this:
#ContextConfiguration(classes=CAPRepositoryConfig.class)
public class CAPRepositoryTest {
private SegmentRepository repository;
#Before
public void testRepository() {
repository = new SegmentRepositoryImpl();
}
#Test
public void testRepository() {
Segment seg = repository.findOne(123);
}
}