I'm developing an application in spring boot that needs to connect to two different databases. To do that this I had to manually configure the connections.
Initially everything seemed to work fine but later I realized I had problems with the transactions.
In fact, although I used the transactional annotation on the methods, I realized that every record i was writing on the DB was immediately committed and that if an exception was thrown the rollback did not happen.
It seems as if the transactions were not active.
This is my actually configuration for the first DB.
import java.util.HashMap;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
#Configuration
#EnableConfigurationProperties
#EnableJpaRepositories(
entityManagerFactoryRef = "fooEntityManager",
basePackages = "xx.yy.zz.repository"
)
#EnableTransactionManagement
public class FooConfig {
#Autowired
private Environment env;
#Value("${spring.datasource.primary.jndi-name}")
private String jndiConnection;
#Primary
#Bean(destroyMethod = "")
public DataSource ds() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiConnection);
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean fooEntityManager(DataSource ds) {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(ds());
em.setPackagesToScan(
new String[] { "xx.yy.zz.entity" });
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "none");
properties.put("hibernate.dialect",
env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean(name="transactionManager")
public PlatformTransactionManager fooTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
fooEntityManager(ds()).getObject());
return transactionManager;a
}
}
I also tried to use JTA but on the contrary this time the application has never written on the DB.
This is the jta configuration.
import java.util.HashMap;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
#Configuration
#EnableConfigurationProperties
#EnableJpaRepositories(
basePackages = "xx.yy.zzzz.repository",
entityManagerFactoryRef = "fooEntityManager"
)
#EnableTransactionManagement
public class FooConfig {
#Value("${spring.datasource.primary.jndi-name}")
private String jndiConnection;
#Autowired
private Environment env;
#Primary
#Bean(destroyMethod = "")
public DataSource ds() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiConnection);
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean fooEntityManager(DataSource ds) {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setJtaDataSource(ds());
em.setPackagesToScan(
new String[] { "xx.yy.zz.entity" });
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "none");
properties.put("hibernate.dialect",
env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("hibernate.transaction.jta.platform",
env.getProperty("org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"));
properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean(name="transactionManager")
public PlatformTransactionManager fooTransactionManager() {
JtaTransactionManager transactionManager = new JtaTransactionManager();
return transactionManager;
}
}
That's all the connection config.
edit1:
I tried to use this configuration in an application with only one connection to one DB. It seems to work fine. I wrote a transactional method that insert 10 element in the DB and if i throw an error in this method, nothing is inserted in the DB.
Related
I want to implement a read-only routing for db, I have researched lots of ways to achieve this and came across the following repository.
I couldn't understand the function of the following two methods so I simplified and changed it in my repository in the following way.
transactionManager() & entityManagerFactory(EntityManagerFactoryBuilder builder)
If I keep having these methods I am getting the error , how could I overcome this error?
Thanks.
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create index contact_tag_ix_uuid on contact_tag (uuid)" via JDBC Statement
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigrator.java:581) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigrator.java:526) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applyIndexes(AbstractSchemaMigrator.java:348) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.GroupedSchemaMigratorImpl.performTablesMigration(GroupedSchemaMigratorImpl.java:88) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:220) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:123) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:196) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:85) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:335) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1498) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
package ru.project.master.slave.datasources;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.google.common.collect.Maps;
import ru.project.master.slave.properties.Properties;
import ru.project.master.slave.routing.RoutingDataSource;
#Configuration
#ComponentScan
#EnableJpaRepositories("ru.project.master.slave.dao")
#EnableTransactionManagement
public class DataSourcesConfiguration {
#Autowired
private Properties props;
#Bean(name = "masterDataSource")
#Primary
public DataSource masterDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(props.getProperty("spring.datasource.master.driver-class-name"));
dataSource.setDefaultAutoCommit(false);
dataSource.setUsername(props.getProperty("spring.datasource.master.username"));
dataSource.setPassword(props.getProperty("spring.datasource.master.password"));
dataSource.setUrl(props.getProperty("spring.datasource.master.url"));
return dataSource;
}
#Bean
public RoutingDataSource routingDataSource() {
RoutingDataSource dataSource = new RoutingDataSource();
dataSource.setDefaultTargetDataSource(masterDataSource());
Map<Object, Object> targets = Maps.newHashMap();
targets.put(DataSourceType.MASTER, masterDataSource());
targets.put(DataSourceType.SLAVE, slaveDataSource());
dataSource.setTargetDataSources(targets);
dataSource.afterPropertiesSet();
return dataSource;
}
#Bean(name = "slaveDataSource")
public DataSource slaveDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(props.getProperty("spring.datasource.slave.driver-class-name"));
dataSource.setDefaultAutoCommit(false);
dataSource.setUsername(props.getProperty("spring.datasource.slave.username"));
dataSource.setPassword(props.getProperty("spring.datasource.slave.password"));
dataSource.setUrl(props.getProperty("spring.datasource.slave.url"));
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(masterDataSource());
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(Boolean.parseBoolean(props.getProperty("spring.jpa.generate-ddl")));
Map<String, String> additionalProperties = Maps.newHashMap();
additionalProperties.put("hibernate.dialect", props.getProperty("spring.jpa.dialect"));
LocalContainerEntityManagerFactoryBean factory = builder.dataSource(routingDataSource())
.packages("ru.project.master.slave.orm").persistenceUnit("routingEMF").properties(additionalProperties)
.build();
factory.setJpaVendorAdapter(vendorAdapter);
factory.afterPropertiesSet();
return factory;
}
}
My solution:
But somehow If feels missing because I think I need to implement as have been mentioned here
https://stackoverflow.com/a/75223437/19751568
I need some configuration to provide some hibernate functionality.
Not only that the hibernate.connection.provider_disables_autocommit allows you to make better use of database connections, but it’s the only way we can make this example work since, without this configuration, the connection is acquired prior to calling the determineCurrentLookupKey method TransactionRoutingDataSource.
#Configuration
#ComponentScan
#("io.api.persistence.repository")
#EnableTransactionManagement
public class DataSourcesConfiguration {
#Value("${spring.datasource.master.url}")
private String mstUrl;
#Value("${spring.datasource.master.username}")
private String mstUsername;
#Value("${spring.datasource.master.password}")
private String mstPassword;
#Value("${spring.datasource.slave.url}")
private String slaveUrl;
#Value("${spring.datasource.slave.username}")
private String slaveUsername;
#Value("${spring.datasource.slave.password}")
private String slavePassword;
#Bean
public DataSource dataSource() {
RoutingDataSource masterSlaveRoutingDataSource = new RoutingDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER, masterDataSource());
targetDataSources.put(DataSourceType.SLAVE, slaveDataSource());
masterSlaveRoutingDataSource.setTargetDataSources(targetDataSources);
// Set as all transaction point to master
masterSlaveRoutingDataSource.setDefaultTargetDataSource(masterDataSource());
return masterSlaveRoutingDataSource;
}
public DataSource slaveDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(slaveUrl);
hikariDataSource.setUsername(slaveUsername);
hikariDataSource.setPassword(slavePassword);
return hikariDataSource;
}
#Primary
public DataSource masterDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(mstUrl);
hikariDataSource.setUsername(mstUsername);
hikariDataSource.setPassword(mstPassword);
return hikariDataSource;
}
I get this exception with this Configuration class
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableTransactionManagement//enable tx management
#EnableWebMvc//enables webmvc
//load properties into spring container
#PropertySource("classpath:app.properties")
//all layered classes common package name
#ComponentScan("in.nit")
public class AppConfig {
#Autowired
private Environment env;
//1) DataSource
#Bean
public DataSource ds() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(env.getProperty("db.driver"));
ds.setUrl(env.getProperty("db.url"));
ds.setUsername(env.getProperty("db.user"));
ds.setPassword(env.getProperty("db.pwd"));
return ds;
}
//2)SessionFactory
#Bean
public SessionFactory sf() {
LocalSessionFactoryBean sf = new LocalSessionFactoryBean();
sf.setDataSource(ds());
sf.setHibernateProperties(props());
sf.setPackagesToScan("in.nit.model");
return sf.getObject();
}
//3)HibernateProperties
public Properties props() {
Properties p = new Properties();
p.put("hibernate.dialect", env.getProperty("orm.dialect"));
p.put("hibernate.show_sql", env.getProperty("orm.showsql"));
p.put("hibernate.format_sql",env.getProperty("orm.fmtsql"));
p.put("hibernate.hbm2ddl.auto", env.getProperty("orm.hbm2ddl.auto"));
return p;
}
//4)HibernateTemplate
#Bean
public HibernateTemplate ht() {
HibernateTemplate ht = new HibernateTemplate();
ht.setSessionFactory(sf());
return ht;
}
//5)HibernateTx Manager
#Bean
public HibernateTransactionManager htx() {
HibernateTransactionManager htx = new HibernateTransactionManager();
htx.setSessionFactory(sf());
return htx;
}
//6)ViewResolver
#Bean
public InternalResourceViewResolver ivr() {
InternalResourceViewResolver ivr = new InternalResourceViewResolver();
ivr.setPrefix(env.getProperty("mvc.prefix"));
ivr.setSuffix(env.getProperty("mvc.suffix"));
return ivr;
}
}
and I get the exception
Error creating bean with name 'ht' defined in in.nit.config.AppConfig: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'sessionFactory' is required
obviously it has something to do with session factory
But when i replace the config with the code below it works fine
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableTransactionManagement //enables HtX
#EnableWebMvc // Spring MVC Activated
//load properties into Spring container
#PropertySource("classpath:app.properties")
//all layered classes common package name
#ComponentScan("in.nit")
public class AppConfig2 {
#Autowired
private Environment env;
//1. DataSource
#Bean
public DataSource ds() {
BasicDataSource d=new BasicDataSource();
d.setDriverClassName(env.getProperty("db.driver"));
d.setUrl(env.getProperty("db.url"));
d.setUsername(env.getProperty("db.user"));
d.setPassword(env.getProperty("db.pwd"));
return d;
}
//2. SessionFactrory
#Bean
public LocalSessionFactoryBean sf() {
LocalSessionFactoryBean s=new LocalSessionFactoryBean();
s.setDataSource(ds());
s.setHibernateProperties(props());
//s.setAnnotatedClasses(ShipmentType.class);
s.setPackagesToScan("in.nit.model");
return s;
}
#Bean
public Properties props() {
Properties p=new Properties();
p.put("hibernate.dialect", env.getProperty("orm.dialect"));
p.put("hibernate.show_sql", env.getProperty("orm.showsql"));
p.put("hibernate.format_sql", env.getProperty("orm.fmtsql"));
p.put("hibernate.hbm2ddl.auto", env.getProperty("orm.hbm2ddl.auto"));
return p;
}
//3. HT
#Bean
public HibernateTemplate ht() {
HibernateTemplate h=new HibernateTemplate();
h.setSessionFactory(sf().getObject());
return h;
}
//4. HtxM
#Bean
public HibernateTransactionManager htx() {
HibernateTransactionManager htm=new HibernateTransactionManager();
htm.setSessionFactory(sf().getObject());
return htm;
}
//5. ViewResovler
#Bean
public InternalResourceViewResolver ivr() {
InternalResourceViewResolver v=new InternalResourceViewResolver();
v.setPrefix(env.getProperty("mvc.prefix"));
v.setSuffix(env.getProperty("mvc.suffix"));
return v;
}
}
Can anyone explain why is the exception is being thrown in the first case ?
And both classes AppConfig and AppCOnfig2 seem to be doing the same thing.
Can anyone point what out specifically where and which lines make a difference?
Definition : LocalSessionFactoryBean is the FactoryBean that creates a Hibernate SessionFactory. It provides the SessionFactory object and itself is not a SessionFactory. In case you are getting exception, you are not passing the sessionfactory object and instead passing factoryBean which causes illegalArgumentException.
Original documentation: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/orm/hibernate5/LocalSessionFactoryBean.html
I will show you a little code and then I will ask a question.
SendEmail.java
package com.goode;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
#Component
#NoArgsConstructor
#Data
#AllArgsConstructor
public class SendEmail {
#Value("${email.username}")
private String username;
#Value("${email.password}")
private String password;
#Value("${email.fullAddress}")
private String fullAddress;
#Value("${email.host}")
private String host;
#Value("${email.port}")
private String port;
public boolean send(String toEmail, String subject, String message){
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
Session session = Session.getInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
try {
Message createMessage = new MimeMessage(session);
createMessage.setFrom(new InternetAddress(fullAddress));
createMessage.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(toEmail));
createMessage.setSubject(subject);
createMessage.setText(message);
Transport.send(createMessage);
return true;
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
PropertySourcesPlaceholderConfigurer o = new PropertySourcesPlaceholderConfigurer();
o.setLocation(new ClassPathResource("application.properties"));
return o;
}
}
RootConfig.java
package com.goode.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableJpaRepositories( basePackages = {"com.goode.repository"})
#PropertySource(value = { "classpath:application.properties" })
#EnableTransactionManagement
#Import({ SecurityConfig.class })
#ComponentScan(basePackages = {"com.goode.service", "com.goode.repository", "com.goode.controller", "com.goode.business", "com.goode"})
public class RootConfig {
#Autowired
private Environment environment;
#Autowired
private DataSource dataSource;
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.POSTGRESQL);
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.goode.business");
factory.setDataSource(dataSource());
factory.setJpaProperties(jpaProperties());
return factory;
}
private Properties jpaProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}
}
application.properties
jdbc.driverClassName = org.postgresql.Driver
jdbc.url = jdbc:postgresql://localhost:5432/GoodE
jdbc.username = postgres
jdbc.password = postgres
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
hibernate.show_sql = true
hibernate.format_sql = true
email.username = xx //I replce real data to xx just to this post
email.password = xx
email.fullAddress = xx#xx.com
email.host = xx
email.port = xx
When I try call a send method from SendEmail console shows error:
java.lang.NullPointerException: null
in line:
props.put("mail.smtp.host", host);
So annotation #Value doesn't inject any value from application.properties to private variable in SendEmail. Why is that?
In RootConfig.java I used #Autowired, Environment and #PropertySource but I read somewhere that you can use #PropertySource only in #Configuration class so I tried find another way -> I found #Value, but I have no idea why data from application.properties are not injecting to variable.
I put PropertySourcesPlaceholderConfigurer in SendEmail because I read it's necessary. I am not sure this is the right place for it but placing this in another class e.g. RootConfig didn't help.
May you have any advice where should I search for bug?
This line:
#PropertySource(value = { "classpath:application.properties" })
is unnecessary because all values from application.properties are picked up by default. That being said, you also don't need PropertySourcesPlaceholderConfigurer for that.
Make sure that when calling your send method, that you are not instantiating your SendEmail class as new SendEmail() because that will not work as #Value works only inside of the spring application context.
You must either do an #Autowired or constructor injection (the latter being recommended).
Also this:
#ComponentScan(basePackages = {"com.goode.service", "com.goode.repository", "com.goode.controller", "com.goode.business", "com.goode"})
Can be replaced by this:
#ComponentScan(basePackages = {"com.goode.*"})
I am new to spring, hibernate, c3p0 & ehcache. I am developing an application completely using Java Configuration except web.xml. I have to use second level cache in my app. So I added following code
import net.sf.ehcache.config.CacheConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableCaching
public class CachingConfig implements CachingConfigurer{
#Bean(destroyMethod="shutdown")
public net.sf.ehcache.CacheManager ehCacheManager() {
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setName("myCacheName");
cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
cacheConfiguration.setMaxEntriesLocalHeap(1000);
net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
config.addCache(cacheConfiguration);
return net.sf.ehcache.CacheManager.newInstance(config);
}
#Bean
#Override
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheManager());
}
#Override
public CacheResolver cacheResolver() {
return null;
}
#Override
public CacheErrorHandler errorHandler() {
return null;
}
#Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
}
In web.xml I added it as
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
ApplicationContextConfiguration
CachingConfig
</param-value>
</context-param>
My ApplicationContextConfiguration is
import java.beans.PropertyVetoException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.mchange.v2.c3p0.ComboPooledDataSource;
#Configuration
#EnableTransactionManagement
public class ApplicationContextConfiguration
{
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws IOException {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(pooledDataSource());
entityManagerFactory.setPackagesToScan(new String[] { "*" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
entityManagerFactory.setJpaProperties(additionalProperties());
return entityManagerFactory;
}
#Bean
public ComboPooledDataSource pooledDataSource() throws IOException{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
Properties properties = new Properties();
BufferedReader input = new BufferedReader (new InputStreamReader (getClass().getResourceAsStream("/config/hibernate.properties")));
properties.load(input);
try {
dataSource.setDriverClass(properties.getProperty("hibernate.connection.driver_class"));
} catch (PropertyVetoException e) {
e.printStackTrace();
}
dataSource.setJdbcUrl(properties.getProperty("hibernate.connection.url"));
dataSource.setUser(properties.getProperty("hibernate.connection.username"));
dataSource.setPassword(properties.getProperty("hibernate.connection.password"));
dataSource.setMaxPoolSize(Integer.parseInt(properties.getProperty("hibernate.c3p0.max_size")));
dataSource.setMinPoolSize(Integer.parseInt(properties.getProperty("hibernate.c3p0.min_size")));
dataSource.setCheckoutTimeout(Integer.parseInt(properties.getProperty("hibernate.c3p0.timeout")));
dataSource.setMaxStatements(Integer.parseInt(properties.getProperty("hibernate.c3p0.max_statements")));
dataSource.setIdleConnectionTestPeriod(Integer.parseInt(properties.getProperty("hibernate.c3p0.idle_test_period")));
dataSource.setAcquireIncrement(Integer.parseInt(properties.getProperty("hibernate.c3p0.acquire_increment")));
return dataSource;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() throws IOException {
Properties properties = new Properties();
Properties hibernateProperties = new Properties();
BufferedReader input = new BufferedReader (new InputStreamReader (getClass().getResourceAsStream("/config/hibernate.properties")));
hibernateProperties.load(input);
properties.setProperty("hibernate.dialect", hibernateProperties.getProperty("hibernate.dialect"));
return properties;
}
}
When I try to run I am getting following error
org.hibernate.cache.NoCacheRegionFactoryAvailableException: Second-level cache is used in the application, but property hibernate.cache.region.factory_class is not given, please either disable second level cache or set correct region factory class name to property hibernate.cache.region.factory_class (and make sure the second level cache provider, hibernate-infinispan, for example, is available in the classpath).
I understood that I have to add
<property name="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</property>
but not sure where and how to add it via Java configuration. Can someone help here please.
In addition, do I need to add the following?
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
Assuming that you are able to retrieve the entities and you just want to enable second level cache:
Add following properties in your existing additionalProperties() method.
properties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory");
properties.setProperty("hibernate.cache.use_second_level_cache", "true");
properties.setProperty("hibernate.cache.use_query_cache", "true");
and on the entity you want to enable second level cache place #Cache annotation on that entity ex: #Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
You can remove CachingConfig class as that has nothing to do with enabling second level cache for Hibernate.
I am working from the Spring data tutorial here: http://spring.io/guides/tutorials/data/3/
I am trying to get my Spring 4 + Hibernate app up and running. I have created the following unit test according to the guide:
/**
*
*/
package com.corrisoft.air.db.integration;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import com.corrisoft.air.db.JPAConfiguration;
import com.corrisoft.air.db.repository.PersonsRepository;
import com.corrisoft.air.model.Person;
/**
* #author Corrisoft Android Development
*
*/
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {JPAConfiguration.class})
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class PersonsRepositoryIntegerationTests {
#Autowired
PersonsRepository personsRepository;
#Test
public void thatItemIsInsertedIntoRepoWorks() throws Exception {
int id = 42;
Person person = new Person();
person.setId(id);
person.setFirstName("Zaphod");
person.setLastName("Beeblebrox");
personsRepository.save(person);
Person retrievedPerson = personsRepository.findById(id);
assertNotNull(retrievedPerson);
assertEquals(id, retrievedPerson.getId());
}
}
This tests the repository that I've created here:
/**
*
*/
package com.corrisoft.air.db.repository;
import com.corrisoft.air.model.Person;
import org.springframework.data.repository.CrudRepository;
/**
* #author Corrisoft Android Development
*
*/
public interface PersonsRepository extends CrudRepository<Person, Integer>{
Person findById(long id);
}
And I'm getting the following exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.corrisoft.air.db.repository.PersonsRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1100)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:960)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:855)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
... 28 more
The docs say that a proxy object will be created from the interface if I have the JPAConfuration class like this:
/**
*
*/
package com.corrisoft.air.db;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.corrisoft.air.db.repository.PersonsRepository;
/**
* #author Corrisoft Android Development
*
*/
#Configuration
#EnableJpaRepositories(basePackages = "com.corrisoft.db.repository",
includeFilters = #ComponentScan.Filter(value = { PersonsRepository.class }, type = FilterType.ASSIGNABLE_TYPE))
#EnableTransactionManagement
public class JPAConfiguration {
#Bean
public DataSource dataSource() throws SQLException {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public EntityManagerFactory entityManagerFactory() throws SQLException {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.corrisoft.air.model");
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
I've already found several issues with the tutorial and am thinking this is another.
You are scanning wrong package for repositories:
... basePackages = "com.corrisoft.db.repository" ...
whereas the correct one is
import com.corrisoft.air.db.repository.PersonsRepository;