I need to connect to two different databases from a single application. The trouble is that my appEntityManager does not have a transaction manager associated with it and I am not sure how to do it. The #Primary adminEntityManager is able to use the one provided by spring boot without any trouble as described here.
The configuration above almost works on its own. To complete the
picture you need to configure TransactionManagers for the two
EntityManagers as well. One of them could be picked up by the default
JpaTransactionManager in Spring Boot if you mark it as #Primary. The
other would have to be explicitly injected into a new instance. Or you
might be able to use a JTA transaction manager spanning both.
I have annoted the configuration with
#EnableTransactionManagement
And here is the relavant beans
#Bean
#ConfigurationProperties(prefix = "datasource.app")
public DataSource appDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource.admin")
public DataSource adminDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean appEntityManagerFactory(
final EntityManagerFactoryBuilder builder) {
return builder
.dataSource(appDataSource())
.packages("au.com.mycompany.app.bomcommon.domain")
.persistenceUnit("appPersistentUnit")
.build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean adminEntityManagerFactory(
final EntityManagerFactoryBuilder builder) {
return builder
.dataSource(adminDataSource())
.packages("au.com.mycompany.app.bombatch")
.persistenceUnit("adminPersistentUnit")
.build();
}
//I thought this would do it but I am getting an exception
//No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: appTransactionManager,transactionManager
#Bean
public JpaTransactionManager appTransactionManager(#Qualifier("appEntityManagerFactory") final EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
Update
I ended up doing it a different way. see here.
See if this works:
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource.admin")
public DataSource adminDS() { ... }
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean adminEMF(...) { ... }
#Bean
#Primary
public JpaTransactionManager adminTM(...) { ... }
#Bean
public LocalContainerEntityManagerFactoryBean appEMF(...) { ... }
#Bean
public JpaTransactionManager appTM(...) { ... }
The only change I have made from your configuration is to declare a transaction manager for the admin side explicitly and marked that transaction manager as the default.
See the below changes. It works for me. Created 3 Data Sources, 3 Session Factories, and 3 Transaction Managers. Added these transaction managers in chainedTransaction as per below:
#Configuration
#EnableTransactionManagement
public class HibernateConfiguration implements TransactionManagementConfigurer {
#Bean("chainedTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("transactionManager1") final HibernateTransactionManager transactionManager1,
#Qualifier("transactionManager2") final HibernateTransactionManager transactionManager2,
#Qualifier("transactionManager3") final HibernateTransactionManager transactionManager3) {
return new ChainedTransactionManager(transactionManager1, transactionManager2, transactionManager3);
}
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
// TODO Auto-generated method stub
return transactionManager(oneTransactionManager(), twoTransactionManager(), threeTransactionManager());
}
#Bean(name = "oneDataSource", destroyMethod="")
public DataSource oneDataSource() throws IllegalArgumentException, NamingException, SQLException {
HikariConfig hkConfig = new HikariConfig();
System.out.println(env.getRequiredProperty("spring1-datasource.jdbcUrl"));
hkConfig.setJdbcUrl(env.getRequiredProperty("spring1-datasource.jdbcUrl"));
hkConfig.setUsername(env.getRequiredProperty("spring1-datasource.username"));
hkConfig.setPassword(env.getRequiredProperty("spring1-datasource.password"));
hkConfig.setDriverClassName(env.getRequiredProperty("spring1-datasource.driverClassName"));
return new HikariDataSource(hkConfig);
}
#Bean(name="oneSessionFactory")
#Qualifier("oneSessionFactory")
public LocalSessionFactoryBean oneSessionFactory() throws IllegalArgumentException, NamingException, SQLException {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(oneDataSource());
sessionFactory.setPackagesToScan(env.getRequiredProperty("entitymanager.packagesToScan"));
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
hibernateProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
#Bean(name="transactionManager1")
public HibernateTransactionManager oneTransactionManager() {
HibernateTransactionManager oneTransactionManager = new HibernateTransactionManager();
try {
oneTransactionManager.setSessionFactory(oneSessionFactory().getObject());
} catch (IllegalArgumentException | NamingException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return oneTransactionManager
}
#Bean(name = "twoDataSource", destroyMethod="")
public DataSource twoDataSource() throws IllegalArgumentException, NamingException, SQLException {
HikariConfig hkConfig = new HikariConfig();
System.out.println(env.getRequiredProperty("spring2-datasource.jdbcUrl"));
hkConfig.setJdbcUrl(env.getRequiredProperty("spring2-datasource.jdbcUrl"));
hkConfig.setUsername(env.getRequiredProperty("spring2-datasource.username"));
hkConfig.setPassword(env.getRequiredProperty("spring2-datasource.password"));
hkConfig.setDriverClassName(env.getRequiredProperty("spring2-datasource.driverClassName"));
return new HikariDataSource(hkConfig);
}
#Bean(name="twoSessionFactory")
#Qualifier("twoSessionFactory")
public LocalSessionFactoryBean twoSessionFactory() throws IllegalArgumentException, NamingException, SQLException {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(twoDataSource());
sessionFactory.setPackagesToScan(env.getRequiredProperty("entitymanager.packagesToScan"));
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
hibernateProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
#Bean(name="transactionManager2")
public HibernateTransactionManager twoTransactionManager() {
HibernateTransactionManager twoTransactionManager = new HibernateTransactionManager();
try {
twoTransactionManager.setSessionFactory(twoSessionFactory().getObject());
} catch (IllegalArgumentException | NamingException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return twoTransactionManager
}
#Bean(name = "threeDataSource", destroyMethod="")
public DataSource threeDataSource() throws IllegalArgumentException, NamingException, SQLException {
HikariConfig hkConfig = new HikariConfig();
System.out.println(env.getRequiredProperty("spring3-datasource.jdbcUrl"));
hkConfig.setJdbcUrl(env.getRequiredProperty("spring3-datasource.jdbcUrl"));
hkConfig.setUsername(env.getRequiredProperty("spring3-datasource.username"));
hkConfig.setPassword(env.getRequiredProperty("spring3-datasource.password"));
hkConfig.setDriverClassName(env.getRequiredProperty("spring3-datasource.driverClassName"));
return new HikariDataSource(hkConfig);
}
#Bean(name="threeSessionFactory")
#Qualifier("threeSessionFactory")
public LocalSessionFactoryBean threeSessionFactory() throws IllegalArgumentException, NamingException, SQLException {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(threeDataSource());
sessionFactory.setPackagesToScan(env.getRequiredProperty("entitymanager.packagesToScan"));
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
hibernateProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
#Bean(name="transactionManager3")
public HibernateTransactionManager threeTransactionManager() {
HibernateTransactionManager threeTransactionManager = new HibernateTransactionManager();
try {
threeTransactionManager.setSessionFactory(threeSessionFactory().getObject());
} catch (IllegalArgumentException | NamingException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return threeTransactionManager
}
}
Related
I'm trying to implement the OpenSessionInView pattern (antipattern) with Spring 5. I configured via Java (not xml) the OpenSessionInView in my WebMvcConfigurer, and watching the logs it seems that is already running. But when I try to load a lazy collection it says the known "Could not write JSON: failed to lazily initialize a collection of role:..."
The crazy stuff is that I see in my logs:
DEBUG [http-nio-8080-exec-7] (OpenSessionInViewInterceptor.java:128) - Opening Hibernate Session in OpenSessionInViewInterceptor
DEBUG [http-nio-8080-exec-7] (AbstractPlatformTransactionManager.java:370) - Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findById]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
DEBUG [http-nio-8080-exec-7] (JpaTransactionManager.java:393) - Opened new EntityManager [SessionImpl(1867707287<open>)] for JPA transaction
DEBUG [http-nio-8080-exec-7] (DriverManagerDataSource.java:144) - Creating new JDBC DriverManager Connection to [jdbc:mysql://MYIP:3306/DB_NAME?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Europe/Madrid]
DEBUG [http-nio-8080-exec-7] (DataSourceUtils.java:186) - Setting JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl#2e6ff9ac] read-only
DEBUG [http-nio-8080-exec-7] (TransactionImpl.java:56) - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
...
...
...
WARN [http-nio-8080-exec-7] (AbstractHandlerExceptionResolver.java:199) - Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: com....Route.styles, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role...
DEBUG [http-nio-8080-exec-7] (OpenSessionInViewInterceptor.java:153) - Closing Hibernate Session in OpenSessionInViewInterceptor
So, it seems that the session is opened as it should, is not closed when the exception is thrown. But my EntityManager is not using that session... Am I correct? How can I achieve that?
Thanks!
EDIT:
My WebConfig.java:
#Configuration
#ComponentScan
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Value( "${resources.files.location}" )
private String fileLocation;
#Autowired
DataSource datasource;
#Autowired
private Environment env;
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("Messages");
messageSource.setCacheMillis(10);
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.getDefault());
return localeResolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
localeInterceptor.setIgnoreInvalidLocale(true);
localeInterceptor.setParamName("idioma");
return localeInterceptor;
}
#Bean
public PlatformTransactionManager hibernateTransactionManager() {
HibernateTransactionManager transactionManager
= new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactoryBean().getObject());
return transactionManager;
}
#Bean
public ViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix("/WEB-INF/views/");
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
public void addInterceptors(InterceptorRegistry registry) {
OpenSessionInViewInterceptor openSessionInViewInterceptor = new OpenSessionInViewInterceptor();
openSessionInViewInterceptor.setSessionFactory(sessionFactoryBean().getObject());
registry.addWebRequestInterceptor(openSessionInViewInterceptor).addPathPatterns("/**");
registry.addInterceptor(new miLoggerInterceptor());
registry.addInterceptor(localeChangeInterceptor());
}
#Bean
public FilterRegistrationBean openEntityManagerInViewFilter() {
FilterRegistrationBean reg = new FilterRegistrationBean();
reg.setName("OpenEntityManagerInViewFilter");
reg.setFilter(new OpenEntityManagerInViewFilter());
return reg;
}
#Bean
public LocalSessionFactoryBean sessionFactoryBean() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(datasource);
sessionFactory.setPackagesToScan("my.package");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
private Properties hibernateProperties() {
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
jpaProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
jpaProperties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
return jpaProperties;
}
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/img/**").addResourceLocations("/img/");
registry.addResourceHandler("/.well-known/acme-challenge/**").addResourceLocations("/.well-known/acme-challenge/");
registry.addResourceHandler("/webfonts/**").addResourceLocations("/webfonts/");
registry.addResourceHandler("/multimedia/").addResourceLocations("file:"+fileLocation);
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/files/**").addResourceLocations("file:///C:/tmp/images/");
registry.addResourceHandler("/favico.ico").addResourceLocations("/favico.ico");
}
}
BusinessConfig.java:
#Configuration
#ComponentScan
#PropertySources({
#PropertySource("classpath:application.properties"),
})
#EnableJpaRepositories("com.muskers.web.business.repositories")
public class BusinessConfig {
#Autowired
private Environment env;
#Bean
public DataSource dataSource() {
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
public PasswordEncoder delegatingPasswordEncoder() {
PasswordEncoder defaultEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(
"bcrypt", encoders);
passwordEncoder.setDefaultPasswordEncoderForMatches(defaultEncoder);
return passwordEncoder;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource(dataSource());
emf.setPackagesToScan(User.class.getPackage().getName());
HibernateJpaVendorAdapter hibernateJpa = new HibernateJpaVendorAdapter();
hibernateJpa.setDatabase(Database.MYSQL);
hibernateJpa.setDatabasePlatform(env.getProperty("hibernate.dialect"));
hibernateJpa.setGenerateDdl(env.getProperty("hibernate.generateDdl", Boolean.class));
hibernateJpa.setShowSql(env.getProperty("hibernate.show_sql", Boolean.class));
emf.setJpaVendorAdapter(hibernateJpa);
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
jpaProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
jpaProperties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
emf.setJpaProperties(jpaProperties);
return emf;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager txnMgr = new JpaTransactionManager();
txnMgr.setEntityManagerFactory(entityManagerFactory().getObject());
return txnMgr;
}
#PostConstruct
public void setTimeZone() {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Madrid"));
}
public class Roles {
public final static String ROLE_USER = "USER";
public final static String ROLE_ADMIN = "ADMIN";
}
public class Authorities {
public final static String MANAGE_GYMS = "MANAGE_GYMS";
public final static String MANAGE_USERS = "MANAGE_USERS";
public final static String READ_GYMS = "READ_GYMS";
public final static String CREATE_GYMS = "CREATE_GYMS";
public final static String CREATE_ROUTES = "CREATE_ROUTES";
public final static String READ_ROUTES = "READ_ROUTES";
public final static String MANAGE_ROUTES = "MANAGE_ROUTES";
}
}
App.java:
public class App extends AbstractAnnotationConfigDispatcherServletInitializer
{
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { BusinessConfig.class, WebSecurityConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
return new Filter[] { characterEncodingFilter};
}
}
I want to configure Spring application with Hibernate. I tried this:
Main start method:
#Configuration
#EnableWebMvc
public class WebConfig implements WebApplicationInitializer, WebMvcConfigurer {
private BasicAuthenticationInterceptor basicAuthenticationInterceptor;
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(converter -> converter instanceof MappingJackson2XmlHttpMessageConverter);
converters.removeIf(converter -> converter instanceof MappingJackson2HttpMessageConverter);
converters.add(new MappingJackson2XmlHttpMessageConverter(
((XmlMapper) createObjectMapper(Jackson2ObjectMapperBuilder.xml()))
.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)));
converters.add(new MappingJackson2HttpMessageConverter(
createObjectMapper(Jackson2ObjectMapperBuilder.json())));
}
private ObjectMapper createObjectMapper(Jackson2ObjectMapperBuilder builder) {
builder.indentOutput(true);
builder.modules(new JaxbAnnotationModule());
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.defaultUseWrapper(false);
return builder.build();
}
#Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(ContextDatasource.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
#Autowired
public void setBasicAuthenticationInterceptor(BasicAuthenticationInterceptor basicAuthenticationInterceptor) {
this.basicAuthenticationInterceptor = basicAuthenticationInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(basicAuthenticationInterceptor);
}
}
Hibernate configuration called from rootContext.register(ContextDatasource.class);:
#SpringBootApplication
#Configuration
#EnableTransactionManagement
public class ContextDatasource {
#Bean
public FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
return new FastJsonHttpMessageConverter();
}
#Bean
#Autowired
public HttpMessageConverters convertersToBeUsed(FastJsonHttpMessageConverter converter) {
return new HttpMessageConverters(converter);
}
#Bean
public LocalSessionFactoryBean getSessionFactory() throws NamingException {
final LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setPackagesToScan(new String[] { "org.datalis.plugin.database.models" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource getDataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup("java:/global/production_gateway");
}
#Bean
public PlatformTransactionManager getHibernateTransactionManager() throws NamingException {
final HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(getSessionFactory().getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor getExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private final Properties hibernateProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.MariaDBDialect");
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.format_sql", "true");
return hibernateProperties;
}
}
But when I deploy the WAR file I get error:
Caused by: java.lang.NoSuchMethodException: org.springframework.boot.autoconfigure.http.HttpMessageConverters$$EnhancerBySpringCGLIB$$1d90bff9.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3302)
Full error stack:
https://pastebin.com/x30W2aws
Can you give advice where I'm wrong and how to fix the problem?
Do I need to implement the module startup with another configuration?
EDIT:
With Java 8 the code is working without above issue. With latest Java 10 I get the above exception. Do you know what configuration I need to do?
According to the Spring Boot release notes, Java 10 is supported by Spring Boot version 2.0.1 and up. Without a list of your dependencies it's impossible to know if this is the issue, but it does seem like a good place to start.
Are you running Boot v2.0.1 or higher?
I'm attempting to transactionally interact with my MySQL database through JPA and Spring data.
config
#Configuration
#EnableJpaRepositories("com.some.models.repositories")
#EnableTransactionManagement
public class MegabotsConfiguration {
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setJpaProperties(additionalProperties());
factory.setPackagesToScan(new String[] { "com.datarank.megabots.models.entities", "com.datarank.megabots.models.repositories" });
factory.setDataSource(dataSource());
factory.afterPropertiesSet();
return factory.getObject();
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/local_ttaggdb");
dataSource.setUsername( "root" );
dataSource.setPassword( "" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return properties;
}
//...
}
And here's where I'm attempting to use a transaction:
#Repository
#Scope("prototype")
#Transactional
public class BotWorkQueueRepository {
#Autowired
private BotWorkQueueJPARepository botWorkQueueJPARepository; //this is the jparepository referenced in the #EnableJpaRepositories in config
#PersistenceContext
private EntityManager entityManager;
#Transactional
public synchronized void flagInFlight(final List<BotWorkQueueEntity> botWorkQueueEntities) {
try {
//...set a few properties on entities, then
entityManager.flush(); //Exception thrown here
entityManager.clear();
} catch (Exception e) {
LOGGER.warn("Error making in flight: " + e);
}
}
}
The error I'm getting:
javax.persistence.TransactionRequiredException: No transactional EntityManager available
I've tried most of the solutions for this error posted in SO, with no success.
One thing for starter,
In maven, if you are using the appassembler-maven-plugin, add the following line to your plugin.configuration tag in the POM file.
<extraJvmArguments>-javaagent:"$REPO"/org/aspectj/aspectjweaver/1.8.5/aspectjweaver-1.8.5.jar</extraJvmArguments>
If you are running in an IDE add the following line to your VM arguments.
-javaagent:<PATH TO MAVEN REPO>/org/aspectj/aspectjweaver/1.8.5/aspectjweaver-1.8.5.jar
Where path to maven repo may look something like /User/kenny/.m2/repository/.
$REPO will be auto-resolved in the POM file.
I try to setup a project with spring-test using TestNg in Maven. The code is like:
#ContextConfiguration(classes={WebMvcTestConfig.class})
public class MyResourceParserTest extends AbstractTestNGSpringContextTests {
#BeforeMethod
public void setup() throws Exception {
}
A WebMvcTestConfig class simply defined a bean:
#Configuration
#ComponentScan(basePackages={"com.test.myapp.model"})
#EnableTransactionManagement
public class WebMvcTestConfig {
private static final String relativeConfigURI = "\\MyAppSpringConfig\\";
private static final String userHomeURI = System.getProperty("user.home");
private static final String jdbcPropertiesFileName = "jdbc.properties";
private static final String hibernatePropertiesFileName = "hibernate.properties";
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setLocation(new FileSystemResource(userHomeURI + relativeConfigURI + jdbcPropertiesFileName));
return propertySourcesPlaceholderConfigurer;
}
#Bean
public DataSource dataSource(
#Value("${jdbc.driverClassName}") String driverClass,
#Value("${jdbc.url}") String jdbcUrl,
#Value("${jdbc.username}") String username,
#Value("${jdbc.password}") String password) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(jdbcUrl);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalSessionFactoryBean localSessionFactoryBean() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource("", "", "", ""));
sessionFactory.setPackagesToScan("com.test.myapp.model.domain");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
try {
hibernateProperties.load(new FileInputStream(userHomeURI + relativeConfigURI + hibernatePropertiesFileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
return hibernateProperties;
}
#Bean
public PlatformTransactionManager platformTransactionManager() {
return new HibernateTransactionManager(localSessionFactoryBean().getObject());
}
}
I got error for Failed to load ApplicationContext when running mvn test from command line:
java.lang.IllegalStateException: Failed to load ApplicationContext
Can you help me?
3 things I notice from your configuration.
Your loading of configuration files is strange, use Spring for that instead of rolling your own. Simply use #PropertySource for that.
The propertySourcesPlaceholderConfigurer() must be static
Your call to datasource will lead to an invalid datasource configuration.
Try the following configuration class.
#Configuration
#ComponentScan(basePackages={"com.test.myapp.model"})
#EnableTransactionManagement
#PropertySource({"${user.home}/MyAppSpringConfig/jdbc.properties","${user.home}/MyAppSpringConfig/hibernate.properties"}
public class WebMvcTestConfig {
#Autowired
private Environment env;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getRequiredProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("env.username", ""));
dataSource.setPassword(env.getProperty("env.password", ""));
return dataSource;
}
#Bean
public LocalSessionFactoryBean localSessionFactoryBean() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan("com.test.myapp.model.domain");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
// Other properties
return hibernateProperties;
}
#Bean
public PlatformTransactionManager platformTransactionManager() {
return new HibernateTransactionManager(localSessionFactoryBean().getObject());
}
}
I am running into some configuration issue since trying to switch to javaconfig from xml config.
Here is the problematic configuration class:
#Configuration
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#Profile({ "default", "cloud" })
public class DataConfiguration {
#Value("${database.driverClassName}")
private String driverClassName;
#Value("${database.url}")
private String url;
#Value("${database.username}")
private String username;
#Value("${database.password}")
private String password;
#Value("${database.validationQuery}")
private String validationQuery;
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setTestOnBorrow(Boolean.TRUE);
dataSource.setTestOnReturn(Boolean.TRUE);
dataSource.setTestWhileIdle(Boolean.TRUE);
dataSource.setTimeBetweenEvictionRunsMillis(1800000);
dataSource.setNumTestsPerEvictionRun(3);
dataSource.setMinEvictableIdleTimeMillis(1800000);
dataSource.setValidationQuery(validationQuery);
dataSource.setMaxActive(5);
dataSource.setLogAbandoned(Boolean.TRUE);
dataSource.setRemoveAbandoned(Boolean.TRUE);
dataSource.setRemoveAbandonedTimeout(10);
return dataSource;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
#Bean
public HibernateJpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Bean
public HibernatePersistence persistenceProvider() {
return new HibernatePersistence();
}
#Bean(name = "entityManagerFactory", destroyMethod = "close")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setPackagesToScan("com.bignibou.domain");
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider());
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
return entityManagerFactoryBean.getObject();
}
public Map<String, String> propertiesMap() {
Map<String, String> propertiesMap = new HashMap<>();
propertiesMap.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
propertiesMap.put("hibernate.hbm2ddl.auto", "update");
propertiesMap.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
propertiesMap.put("hibernate.connection.charSet", "UTF-8");
propertiesMap.put("hibernate.show_sql", "true");
propertiesMap.put("hibernate.format_sql", "true");
propertiesMap.put("hibernate.use_sql_comments", "true");
return propertiesMap;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Here is the exception I get:
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public static javax.persistence.EntityManager org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(javax.persistence.EntityManagerFactory)] threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:181)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:570)
... 121 more
Caused by: java.lang.NullPointerException
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.initProxyClassLoader(SharedEntityManagerCreator.java:151)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.<init>(SharedEntityManagerCreator.java:143)
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.java:118)
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.java:96)
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:160)
... 122 more
It seems there's an issue with the entityManagerFactory config... What I am getting wrong?
If you are not using the entityManagerFactory() method anywhere in your java configuration, you can instead return the LocalContainerEntityManagerFactoryBean object.
The LocalContainerEntityManagerFactoryBean is both an InitializingBean and a FactoryBean. These are special interfaces that Spring can use to initialize a bean and then add it to the context.
You could therefore change your method to
#Bean(name = "entityManagerFactory", destroyMethod = "close")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setPackagesToScan("com.bignibou.domain");
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider());
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
return entityManagerFactoryBean;
}
Spring will take care of calling afterPropertiesSet() and getObject() on the object returned by the method and adding the created EntityManagerFactory bean to the context.
This is detailed in the IoC chapter of the Spring documentation.
adding the following line sorted the issue:
entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
entityManagerFactoryBean.afterPropertiesSet();//NOTICE HERE!!!
return entityManagerFactoryBean.getObject();