I am trying to use Spring Data JPA with MyBatis. Since there isnt a Vendor Adapter for MyBatis, what is the alternative here?
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.abc.xyz.domain"/>
</bean>
I am getting the below exception when I tried to initialize my application.
Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: No PersistenceProvider specified in EntityManagerFactory configuration, and chosen PersistenceUnitInfo does not specify a provider class name either
Thanks
Mybatis does not implement JPA.
Mybatis is not ORM Framework. JPA is ORM Specification which is implemented by Hibernate, Toplink, Eclipselink . Since Mybatis does not mplement JPA, it does not come under the list of JPA providers.
Hence, you cannot use mybatis as a JPA framework.
Mybatis is a data mapper framework which is completely different framework compared to JPA.
In JPA and ORM frameworks, you map Objects /Entities to the corresponding sql tables and you work on objects and not on tables directly unless you use their native queries.
In mybatis , you play directly with sql data..
Hope this clears the difference between mybatis and JPA.
Hence when you want mybatis with spring data you use spring data mybatis independently and not spring data JPA.
Why not try spring-data-jpa-extra
It provide a dynamic query solution for spring-data-jpa like mybatis, but much easier than mybatis.
I think you would like it : )
Spring Data MyBatis
If you would not like to use a JPA implementation like Spring-Data-JPA module, but you like use Spring-Data you can find Spring-Data-Mybatis a useful project.
I know that this is not precise answer to your question but I hope that this answer can be interesting.
Spring-Data-Mybatis Hatunet version
I am using this project: https://github.com/hatunet/spring-data-mybatis
It fit very well with spring-data-mybatis and it have also the paginated repository.
Work very well on production project.
update 08/2020
The projet as moved to another webspace and evolved: https://github.com/easybest/spring-data-mybatis
Here is the configuration of mybatis and jpa in spring framework. Mybatis and jpa are different framework so you cannot use mybatis as a JPA framework. Feel free to ask any question if you cannot catch up the configuration.
package com.mastering.springbatch.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
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.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
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 javax.sql.DataSource;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = {"com.mastering.springbatch.dao",
"com.mastering.springbatch.repository"})
#EntityScan("com.mastering.springbatch.entity")
public class DataConfig {
private final String ENTITY_PACKAGE = "com.mastering.springbatch.entity";
private DriverManagerDataSource dataSource;
#Primary
#Bean(value = "customDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
this.dataSource = dataSource;
return dataSource;
}
#Primary
#Bean(value = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean emf =
new LocalContainerEntityManagerFactoryBean();
emf.setPackagesToScan(ENTITY_PACKAGE);
emf.setDataSource(dataSource());
emf.setJpaVendorAdapter(jpaVendorAdapter());
return emf;
}
#Primary
#Bean(value = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
return factoryBean.getObject();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager() {
JpaTransactionManager tm = new JpaTransactionManager();
tm.setEntityManagerFactory(entityManagerFactory().getObject());
return tm;
}
private JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
return jpaVendorAdapter;
}
}
here is the build.gradle file
buildscript {
ext {
springBootVersion = '2.1.8.RELEASE'
springBootDepManagementVersion = '1.0.8.RELEASE'
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath "io.spring.gradle:dependency-management-plugin:${springBootDepManagementVersion}"
}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'java'
apply plugin: 'idea'
group 'com.learning'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
configurations {
implementation.exclude module: "spring-boot-starter-tomcat"
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile 'org.mybatis:mybatis:3.5.0'
compile 'org.mybatis:mybatis-spring:2.0.0'
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-batch")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("mysql:mysql-connector-java:8.0.14")
// implementation("io.springfox:springfox-swagger2:2.7.0")
// implementation("io.springfox:springfox-swagger-ui:2.7.0")
implementation("org.projectlombok:lombok:1.18.10")
annotationProcessor("org.projectlombok:lombok:1.18.10")
compile group: 'commons-io', name: 'commons-io', version: '2.6'
testAnnotationProcessor("org.projectlombok:lombok:1.18.10")
testCompile("junit:junit:4.12")
testCompile("org.mockito:mockito-core:2.1.0")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
springBoot {
buildInfo()
}
Related
I am facing an issue that has been mentioned before with Spring Boot vs. Hibernate Validation, where autowiring of dependencies inside custom Constraint Validators is not working. From my own debugging, I have noticed that when entity-level validation occurs, Hibernate loads a different ConstraintValidatorManager compared to when Hibernate is performing bean validation for form submits. The latter works fine, the former leads to dependencies of the custom Constraint Validator being null. It seems as if Hibernate is loading one manager from the root context and one from the servlet context. This would explain Hibernate not having any knowledge of the existence of the dependencies autowired in the custom Constraint Validator. If this is true however, I do not understand what is going on, or how to make Hibernate/JPA aware of the Spring context and it's beans.
I am hoping someone could point me in the right direction? I have tried all of the below answers, and much more (e.g. different library versions, configuration methods, static bean loading through a utils class, etc.):
Inject Repository inside ConstraintValidator with Spring 4 and message interpolation configuration
Autowired gives Null value in Custom Constraint validator
Also I have been through the Reference guide for Spring Boot specifically several times, without much luck. There are several cases that mention their Hibernate validation working fine, both for regular bean submits, as well as during entity persisting. Unfortunately, I seem unable to retrieve their exact (Java) configuration they used, but it seems they are using default configuration. I am starting to wonder if this is a specific Spring Boot issue (although it is stated a combination of Spring Validation and Hibernate Validation should work out-of-the-box).
Adding anything like below bean does not solve the issue (default factory being SpringConstraintValidatorFactory ofcourse):
#Bean
public LocalValidatorFactoryBean validator()
{
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
Nor does including a bean definition for a Hibernate validator as such:
Autowired gives Null value in Custom Constraint validator
There are many different ways of loading and injecting the desired bean, but if Hibernate is not at all aware of the beans loaded in the context (because it is using a different context?), how to proceed?
Thanks in advance.
UPDATE: Gradle file
buildscript {
ext {
springBootVersion = '2.1.5.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = '<hidden>'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('org.springframework.boot:spring-boot-starter-tomcat:2.1.5.RELEASE')
implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
implementation('org.springframework.boot:spring-boot-starter-security')
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-mail')
implementation('org.springframework.session:spring-session-core')
annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')
implementation('org.postgresql:postgresql')
// https://mvnrepository.com/artifact/org.jboss.aerogear/aerogear-otp-java
implementation('org.jboss.aerogear:aerogear-otp-java:1.0.0')
implementation('com.github.mkopylec:recaptcha-spring-boot-starter:2.2.0')
implementation('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.0.5')
implementation('org.thymeleaf.extras:thymeleaf-extras-springsecurity3:3.0.4.RELEASE')
implementation('javax.enterprise:cdi-api:2.0')
runtimeOnly('org.springframework.boot:spring-boot-devtools')
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation('org.springframework.security:spring-security-test')
testImplementation 'org.mockito:mockito-core:2.27.0'
}
There is a way to tell Hibernate to use the same validator by setting javax.persistence.validation.factory
#Configuration
#Lazy
class SpringValidatorConfiguration {
#Bean
#Lazy
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(final Validator validator) {
return new HibernatePropertiesCustomizer() {
#Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("javax.persistence.validation.factory", validator);
}
};
}
}
That way everything works fine.
Regarding the fix, just to summarize a more extensive/integrated answer for others that are/were dealing with these sorts of issues, my configuration now contains all of these beans:
#Bean
public LocalValidatorFactoryBean validator()
{
LocalValidatorFactoryBean validatorFactory = new LocalValidatorFactoryBean();
validatorFactory.setValidationMessageSource(messageSource());
return validatorFactory;
}
#Bean
public MessageSource messageSource()
{
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor()
{
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator());
return methodValidationPostProcessor;
}
#Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer()
{
return properties ->
{
properties.put("javax.persistence.validation.factory", validator());
// Add more properties here such as validation groups (see comment for SO example)
};
}
For an example on adding hibernate validation groups to tease apart validation of different life-cycle events (e.g. bean vs. entity), see Hibernate validations on save (insert) only
For regulatory and security reasons I had to split the logic of my Spring boot application into two tools: One for administration with limited number of tables and one for the "real" user application. Therefore I have two MySQL databases instances on the server version 5.7. While the user tool only accesses one database with dozens of tables, the admin tool needs to access entities in both databases.
The tools are both JavaFX and Spring Boot based. Because of this architectural setup I have three maven packages: One for the admin tool and all the admin related entities, services and alike, one for the user tool and all related entities, services etc. only relevant for this user tool, and the third with all the entities that both tools share.
When I run the user tool it generates the tables in the shared database and uses the hibernate ImprovedNamingStrategy based on the configuration in its application.properties file. Hence the columns have an underscore where appropriate.
In the first place the admin tool wouldn't create any database tables at all using spring.jpa.hibernate.ddl-auto, but I had to use spring.jpa.generate-ddl.
Now, when I run the admin tool I would expect that it only creates the tables in the admin database since this datasource is annotated as #Primary. But it also creates columns in the user database with mixed case. Hence I have columns named e.g. "email_address" and "emailAddress" in user database.
I wonder whether any properties are used with my approach? Any ideas how to do it properly?
Please find following some source..
application.properties :
# Employee database
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.jdbcUrl=jdbc:mysql://127.0.0.1/agiletunesdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&characterSetResults=utf-8
spring.datasource.username=YYY
spring.datasource.password=XXXXXX
# Account database
security.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
security.datasource.jdbcUrl=jdbc:mysql://127.0.0.1/authenticationdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&characterSetResults=utf-8
security.datasource.username=YYY
security.datasource.password=XXXXXX
# create db schema, values are none, validate, update, create, and create-drop.
#spring.jpa.hibernate.ddl-auto=create
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=true
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
Databases Configurations :
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class MultipleDbConfiguration {
/*
* Persistence of admin database
*/
#Bean(name = "securityDB")
#Primary
#ConfigurationProperties(prefix="security.datasource")
public DataSource securityDataSource() {
return DataSourceBuilder.create().build();
}
/*
*
* Persistence of user database
*/
#Bean(name = "organizationDB")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource organizationDataSource() {
return DataSourceBuilder.create().build();
}
}
The user database configuration
import java.util.HashMap;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "organizationEntityManagerFactory",
transactionManagerRef = "organizationTransactionManager",
basePackages = "com.agiletunes.domain.organization"
)
#EnableTransactionManagement
#PropertySources({ #PropertySource("classpath:application.properties") })
public class OrganizationConfig {
#Autowired
private Environment env; // Contains Properties Load by #PropertySources
#Bean(name = "organizationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean organizationEntityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("organizationDB") DataSource dataSource) {
HashMap<String, Object> properties = new HashMap<>();
properties.put("spring.jpa.properties.hibernate.dialect", env.getProperty("spring.jpa.properties.hibernate.dialect"));
properties.put("spring.jpa.hibernate.ddl-auto", env.getProperty("spring.jpa.hibernate.ddl-auto"));
properties.put("spring.jpa.hibernate.naming-strategy", env.getProperty("spring.jpa.hibernate.naming-strategy"));
return builder
.dataSource(dataSource)
.packages("com.agiletunes.domain.organization")
.persistenceUnit("organizationPU")
.properties(properties)
.build();
}
#Bean(name="organizationTransactionManager")
public PlatformTransactionManager secondTransactionManager(#Qualifier("organizationEntityManagerFactory")
EntityManagerFactory organizationEntityManagerFactory) {
return new JpaTransactionManager(organizationEntityManagerFactory);
}
}
The trick was to use configuration classes which would have a
#PropertySources({ #PropertySource("classpath:application.properties") })
annotation. Then, in the method which creates the LocalContainerEntityManagerFactoryBean, you can pull and set values defined in your application.properties file with
properties.put("spring.jpa.hibernate.naming.physical-strategy", env.getProperty("spring.jpa.hibernate.naming.physical-strategy"));
I have created a POC with Spring Boot and JPA using spring-boot-starter-data-jpa and my solution works pretty well. My issue arises when i try to add the same solution to an old project which uses Spring 4 and hibernate JPA 2.1 version. When I deployed my war in weblogic, the first error i got was could not autowire the repository so i added the the #EnableJpaRepositories. I even tried to add spring.datasource.jndi-name=EXAMPLE_Data_Source where EXAMPLE_Data_Source is my weblogic datasource but no luck.
The application class:
#Configuration
#EnableWs
#ComponentScan("com.example.package")
#EnableJpaRepositories(basePackages = {"com.example.repository","com.example.entity"})
public class AppConfig extends WsConfigurerAdapter { //WsConfigurerAdapter is spring-ws-core 2.2.0 Release
#Autowire
MyRepository repository;
The Repository class:
public interface MyRepository extends JpaRepository<EntityClass, EntityPKey> {
#Query("select c from EntityClass c where ID = :param1 and TYPE = :param2 and :param3 between FROM_DATE and TO_DATE ")
EntityClass entityClass(#Param("param1") int param1, #Param("param2")
char param2, #Param("param3") Date param3);
}
Now i get the get the error below:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined" weblogic.application.ModuleException:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined
at weblogic.application.internal.ExtensibleModuleWrapper.start(ExtensibleModuleWrapper.java:140)
at weblogic.application.internal.flow.ModuleListenerInvoker.start(ModuleListenerInvoker.java:124)
at weblogic.application.internal.flow.ModuleStateDriver$3.next(ModuleStateDriver.java:233)
at weblogic.application.internal.flow.ModuleStateDriver$3.next(ModuleStateDriver.java:228)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:45)
Truncated. see log file for complete stacktrace
Caused By: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:641)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1157)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:280)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
Truncated. see log file for complete stacktrace
I have to get it working on the old spring project which is deployed in weblogic.
Questions 1: Will this way of solving my issue work for the Spring framework or only with Springboot?
Question 2: What configurations I am missing?
Question 3: Please point me to a working example. I am struggling to make this work.
Spring webmvc and spring-tx is version 4.0.6
Please be a bit more detailed in your explanation. I am still very junior.
Thanks in advance.
You have to configure Below Beans
DriverManagerDataSource
LocalContainerEntityManagerFactoryBean
JpaTransactionManager
Please refer this example
I was using annotation based configuration so i solved it after 2 long days using the following configuration:
#Bean
public DataSource jndiDataSource() {
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
return dsLookup.getDataSource(dataSourceName); // where dataSourceName is your weblogic data source
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(jndiDataSource());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setPackagesToScan("com.example.entity");
return factoryBean;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(false);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.ORACLE);
return hibernateJpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
In a Spring Boot application I'm trying to setup multiple database connections. I've started building the primary datasource, but I'm getting the following error on the mySqlEntityManagerFactory method.
Could not autowire. no beans of EntityManagerFactoryBuilder
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
#Transactional
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "digital.sheppard.dao",
entityManagerFactoryRef = "entityManager",
transactionManagerRef = "transactionManager")
public class PrimaryDBConfig {
#Bean(name="dataSource")
#Primary
#ConfigurationProperties(prefix = "primary.datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#PersistenceContext(unitName = "primary")
#Primary
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(mysqlDataSource()).persistenceUnit("primary").properties(jpaProperties())
.packages("digital.sheppard.model").build();
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("hibernte.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
props.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return props;
}
}
How would I autowire the EntityManagerFactoryBuilder?
I'm trying to follow the code on this blog https://raymondhlee.wordpress.com/2015/10/31/configuring-multiple-jpa-entity-managers-in-spring-boot/
Here's the main application class if it's helpful
#Configuration
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
#ComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
For me the reason was a wrong import, so I advise everybody to look at the often overlooked import section of your configuration class. There is a Hibernate class named the same as the springframework one. Of course you should choose the spring one...
I think you should remove this code
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
The exception is due to public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
which expects a bean of EntityManagerFactoryBuilder.
I checked the reference link, I am not sure if that code will work.
Typically, one creates an instance of LocalContainerEntityManagerFactoryBean and initializes it as per need. In your case you can do
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(mysqlDataSource());
em.setPersistenceUnitName("primary");
em.setPackagesToScan(new String[] { "digital.sheppard.model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(jpaProperties());
The persistence unit name should be same as defined in persistence.xml, though the file is now optional when using Spring JPA.
For a non spring version check out https://stackoverflow.com/a/26814642/776548
Also
since you are initializing EntityManagerFactory by yourself, we will have to exclude DataSourceAutoConfiguration.class.
#Primary is only required if you want multiple datasources. If you have only one, consider removing the annotation, and add it when you need to have multiple data sources
Change parameter name builder to entityManagerFactoryBuilder to inject bean present in JpaBaseConfiguration.class
Have you tried to remove your exclusion of 'DataSourceAutoConfiguration' ?
Using '#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})' prevent a lot of beans from beeing created.
If you got a problem when using a datasource and adding this is your solution, maybe it's not the right one.
Know that spring boot detect the presence of certain classes in the classpath.
If you're using maven, it's reading all classes from all dependencies.
So consider let this DataSourceAutoConfiguration.class running;
cheers
It could be, notice that just could be, your main class is not at the top of the "class tree". Spring needs to scan all classes that are a child (according to package convention) starting from the main class.
Maybe you would read https://www.baeldung.com/spring-component-scanning
If your classes aren't been read by spring scan, they will never be into spring context.
Couple of possibilities :
You need to add the #EnableJpaRepositories(basePackages = {"your.pkg.here"}) to the Application . This tells Spring Data to look for your repository classes under the specified package.
For me , removing #Primary on multiple Data sources, which was added by mistake fixed the issue
it was caused by your ide software,set up these options
I am using #Configuration annotation for configuration of spring instead of xml file. I am configuring 2 datasources with different session factory and different transaction managers. I am stuck with a problem here for #EnableTransactionManagement annotation. I read in its documentation that,
#EnableTransactionManagement is more flexible; it will fall back to a
by-type lookup for any PlatformTransactionManager bean in the
container. Thus the name can be "txManager", "transactionManager", or
"tm": it simply does not matter.
This means whatever name I give to method, it will always search for the method which returns PlatformTransactionManager object while I have 2 transactionmanagers. Now the problem is, when I test this class, it gives me error:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single bean but found 2
I even tried to have 2 different Configuration classes but in vain. In xml configuration, this was not the case. I registered my both transaction managers with two <tx:annotation-driven transaction-manager="" /> tag and it worked fine. But not able to do same here with annotations.
What should I do if I want to configure 2 datasources with 2 different transaction managers in Spring annotated configuration class?
In your configuration class, use #EnableTransactionManagement annotation.
Define a transaction manager in this class as:
#Bean(name="txName")
public HibernateTransactionManager txName() throws IOException{
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(...);
txName.setDataSource(...);
return txName;
}
There on, in your class/method that executes transactional job(s), annotate as follows:
#Transactional("txName")
or
#Transactional(value = "txName")
This is how you would tie a name qualified transaction manager to wherever you need it. You can now have as many transaction managers as you want and use it accordingly wherever you need.
Just in case anyone runs into this problem, I found a solution:
#Configuration
#EnableTransactionManagement
#DependsOn("myTxManager")
#ImportResource("classpath:applicationContext.xml")
public class AppConfig implements TransactionManagementConfigurer {
#Autowired
private PlatformTransactionManager myTxManager;
...
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return this.myTxManager;
}
In this way, you can use a specific txManager defined in an xml configuration.
In case you want to define the txManager used on service-level, you shall remove the #EnableTransactionManagement annotation from the #Configuration class and specify the txManager in the #Transactional annotations, e.g.
#Service
#Transactional(value="myTxManager", readOnly = true)
public class MyServiceImpl implements MyService { ... }
From the java doc
For those that wish to establish a more direct relationship between
#EnableTransactionManagement and the exact transaction manager bean to be used, the
TransactionManagementConfigurer callback interface may be implemented - notice the
implements clause and the #Override-annotated method below:
Your #Configuration class needs to implement TransactionManagementConfigurer interface - implement the annotationDrivenTransactionManager which will return the reference to the transactionManager that should be used.
I am not sure why you are using two TransactionManagers . You could consider using the same TransactionManager for multiple datasource via the AbstractRoutingDataSource . Please refer
http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/
for a sample on its usage.
I have to use JPA and Reactive Mongo in one project. What works at last was:
create a #Configuraition class to explicitly create a JPA transaction manager, like here:
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean dbEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dbDatasource());
em.setPackagesToScan(new String[]{"projectone.mysql"});
em.setPersistenceUnitName("dbEntityManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect",env.getProperty("hibernate.dialect"));
properties.put("hibernate.show-sql",env.getProperty("jdbc.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public DataSource dbDatasource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("spring.datasource.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
#Primary
#Bean
public PlatformTransactionManager jpaTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
dbEntityManager().getObject());
return transactionManager;
}
}
Notice that the bean name jpaTransactionManager, which would be the txManager name used in JPA #Transactional.
create MongoConfiguration to explicitly create a Mongo transaction manager(a lot of beans to define)
in #Transactional, call them with name. The default one transactionManger will not work. You have to distinguish, like jpaTransactionManager and reactiveMongoTransactionManger.
#Transactional(value="jpaTransactionManager")
public void xxx() {
...
}
Note that JPA transaction methods cannot Reactor types as return value(Mono/Flux). Spring will force methods returning Mono/Flux to use ReactiveTransactionManager, it will cause confusion.
Some of the other answers imply that using two transaction managers is in some way wrong; however, Spring's XML configuration allows for using multiple transaction managers as stated in the online documentation (below). Unfortunately, there does not seem to be a way to make the #EnableTransactionManagement annotation work in a similar manner. As a result, I simply use an #ImportResource annotation to load an XML file that includes the <tx:annotation-driven/> line. This allows you to get a Java configuration for most things but still make use of #Transactional with an optional Transaction Manager qualifier.
http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/transaction.html
Most Spring applications only need a single transaction manager, but there may be situations where you want multiple independent transaction managers in a single application. The value attribute of the #Transactional annotation can be used to optionally specify the identity of the PlatformTransactionManager to be used. This can either be the bean name or the qualifier value of the transaction manager bean. For example, using the qualifier notation, the following Java code
Try to use chained TransactionalManager
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
#Configuration
public class ChainedDBConfig {
#Bean("chainedTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("database1TransactionManager") final PlatformTransactionManager db1PlatformTransactionManager,
#Qualifier("database2TransactionManager") final PlatformTransactionManager db2PlatformTransactionManager) {
return new ChainedTransactionManager(db1PlatformTransactionManager, db2PlatformTransactionManager);
}
}
And place the following annotation on your service class:
#Transactional(transactionManager = "chainedTransactionManager")
public class AggregateMessagesJobIntegrationTest {
...
}
You can also use it inside the integration tests:
#RunWith(SpringRunner.class)
#Transactional(transactionManager = "chainedRawAndAggregatedTransactionManager")
#Rollback
public class ExampleIntegrationTest extends AbstractIntegrationTest {
....
}
and it will do a rollback for both DB transaction managers.