I have a simple springboot application managing two MariaDB sources, "users" and "data". I have two distinct configurations:
For users:
package it.unict.spring.application.configurations.user;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class JpaConfigUser {
@Value("${spring.datasource2.driver-class-name}")
private String driver;
@Value("${spring.datasource2.url}")
private String urlUser;
@Value("${spring.datasource2.username}")
private String username;
@Value("${spring.datasource2.password}")
private String password;
@Bean("userSource")
public DataSource getUserSource() {
return DataSourceBuilder.create().driverClassName(driver).url(urlUser)
.username(username).password(password).build();
}
}
and
package it.unict.spring.application.configurations.user;
import java.util.Objects;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
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;
@Configuration
@EnableJpaRepositories(basePackages = { "it.unict.spring.application.repository.user",
"it.unict.spring.application.persistence.model.user" }, entityManagerFactoryRef = "userEntityManagerFactory", transactionManagerRef = "userTransactionManager")
public class JpaTransactUser {
@Bean("userEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean userEntityManagerFactory(
@Qualifier("userSource") DataSource dataSource,
@Qualifier("userJpaVendor") JpaVendorAdapter adapter) {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MariaDB103Dialect");
properties.put(
"spring.jpa.properties.javax.persistence.schema-generation.scripts.action",
"create");
properties.put(
"spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target",
"create.sql");
LocalContainerEntityManagerFactoryBean lem = new LocalContainerEntityManagerFactoryBean();
lem.setPackagesToScan("it.unict.spring.application.repository.user",
"it.unict.spring.application.persistence.model.user");
lem.setJpaProperties(properties);
lem.setJpaVendorAdapter(adapter);
lem.setDataSource(dataSource);
return lem;
}
@Bean("userTransactionManager")
public PlatformTransactionManager userTransactionManager(
@Qualifier("userEntityManagerFactory") LocalContainerEntityManagerFactoryBean topicsEntityManagerFactory) {
return new JpaTransactionManager(
Objects.requireNonNull(topicsEntityManagerFactory.getObject()));
}
@Bean("userJpaVendor")
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true); // Auto creating scheme when true
hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);// Database type
return hibernateJpaVendorAdapter;
}
}
while for data:
package it.unict.spring.application.configurations.data;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class JpaConfigData {
@Value("${spring.datasource.driver-class-name}")
private String driver;
@Value("${spring.datasource.url}")
private String urlData;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Bean("dataSource")
@Primary
public DataSource getDataSource() {
return DataSourceBuilder.create().driverClassName(driver).url(urlData)
.username(username).password(password).build();
}
}
and
package it.unict.spring.application.configurations.data;
import java.util.HashMap;
import java.util.Objects;
import java.util.Properties;
import javax.sql.DataSource;
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.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
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;
@Configuration
@EnableJpaRepositories(basePackages = { "it.unict.spring.application.repository.data",
"it.unict.spring.application.persistence.model.data" }, entityManagerFactoryRef = "dataEntityManagerFactory", transactionManagerRef = "dataTransactionManager")
public class JpaTransactData {
@Bean("dataEntityManagerFactory")
@Primary
public LocalContainerEntityManagerFactoryBean dataEntityManagerFactory(
@Qualifier("dataSource") DataSource dataSource,
@Qualifier("dataJpaVendor") JpaVendorAdapter adapter)
{
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MariaDB103Dialect");
properties.put("spring.jpa.properties.javax.persistence.schema-
generation.scripts.action","create");
properties.put("spring.jpa.properties.javax.persistence.schema-
generation.scripts.create-target", "create.sql");
LocalContainerEntityManagerFactoryBean lem=new
LocalContainerEntityManagerFactoryBean();
lem.setPackagesToScan("it.unict.spring.application.repository.data",
"it.unict.spring.application.persistence.model.data");
lem.setJpaProperties(properties);
lem.setJpaVendorAdapter(adapter);
lem.setDataSource(dataSource);
return lem;
}
@Bean("dataTransactionManager")
@Primary
public PlatformTransactionManager dataTransactionManager(
@Qualifier("dataEntityManagerFactory") LocalContainerEntityManagerFactoryBean topicsEntityManagerFactory) {
return new JpaTransactionManager(
Objects.requireNonNull(topicsEntityManagerFactory.getObject()));
}
@Bean("dataJpaVendor")
@Primary
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true); // Auto creating scheme when true
hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);// Database type
return hibernateJpaVendorAdapter;
}
}
I know that some properties are unuseful, but the problem is that the main application:
package it.unict.spring.application;
import it.unict.spring.application.persistence.model.data.Todo;
import it.unict.spring.application.persistence.model.user.Privilege;
import java.sql.SQLException;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@SpringBootApplication
public class Application {
private static final Logger applogger = LoggerFactory.getLogger("Application");
@Autowired
private ApplicationContext appContext;
@Autowired
public LocalContainerEntityManagerFactoryBean dataEntityManagerFactory;
@Autowired
public LocalContainerEntityManagerFactoryBean userEntityManagerFactory;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@PostConstruct
@Transactional
public void postConstruct() {
applogger.info("Post-construct message: Application started");
System.out.println("Console -- Application started");
try {
applogger.info("Connected to DB schema: "
+ ((DataSource) appContext.getBean("userSource")).getConnection()
.getCatalog());
applogger.info("Connected to DB schema: "
+ ((DataSource) appContext.getBean("dataSource")).getConnection()
.getCatalog());
Todo p = new Todo("admin");
Privilege s = new Privilege("admin");
userEntityManagerFactory.getObject().createEntityManager().persist(s);
dataEntityManagerFactory.getObject().createEntityManager().persist(p);
} catch (SQLException ex) {
applogger.info("Error on DBConnection: " + ex.toString());
}
}
}
throws a
java.lang.IllegalArgumentException: Unknown entity: it.unict.spring.application.persistence.model.user.Privilege
that prevents the deployment of the application due to
userEntityManagerFactory.getObject().createEntityManager().persist(s);
If the above statement is omitted, the application starts but only null values are inserted in the DB.
The Privilege entity is:
package it.unict.spring.application.persistence.model.user;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "privilege", catalog = "users")
public class Privilege implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String name;
//
public Privilege() {
super();
}
public Privilege(String name) {
super();
this.name = name;
}
//
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Privilege [id=").append(id).append(", name=").append(name).append("]");
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((id == null) ? 0 : id.hashCode());
result = (prime * result) + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Privilege other = (Privilege) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
}
Following the comments I modified the configurations as follows. For user database:
package it.unict.spring.application.configurations.user;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
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.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "userEntityManager", transactionManagerRef = "userTransactionManager", basePackages = {
"it.unict.spring.application.repository.user" })
public class JpaTransactUser {
@Bean(name = "userEntityManager")
public LocalContainerEntityManagerFactoryBean getServersEntityManager(
EntityManagerFactoryBuilder builder,
@Qualifier("userSource") DataSource dataDataSource) {
return builder.dataSource(dataDataSource)
.packages("it.unict.spring.application.persistence.model.user")
.persistenceUnit("userUnit").build();
}
@Bean(name = "userTransactionManager")
public JpaTransactionManager transactionManager(
@Qualifier("userEntityManager") EntityManagerFactory serversEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(serversEntityManager);
return transactionManager;
}
}
For data:
package it.unict.spring.application.configurations.data;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
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.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "dataEntityManager", transactionManagerRef = "dataTransactionManager", basePackages = {
"it.unict.spring.application.repository.data" })
public class JpaTransactData {
@Bean(name = "dataEntityManager")
@Primary
public LocalContainerEntityManagerFactoryBean getServersEntityManager(
EntityManagerFactoryBuilder builder,
@Qualifier("dataSource") DataSource dataDataSource) {
return builder.dataSource(dataDataSource)
.packages("it.unict.spring.application.persistence.model.data")
.persistenceUnit("dataUnit").properties(additionalJpaProperties()).build();
}
Map<String, ?> additionalJpaProperties() {
Map<String, String> map = new HashMap<>();
map.put("hibernate.hbm2ddl.auto", "create");
map.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
map.put("hibernate.show_sql", "true");
return map;
}
@Bean(name = "dataTransactionManager")
@Primary
public JpaTransactionManager transactionManager(
@Qualifier("dataEntityManager") EntityManagerFactory serversEntityManager) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(serversEntityManager);
return transactionManager;
}
}
With
@Qualifier("dataEntityManager")
public LocalContainerEntityManagerFactoryBean dataEntityManagerFactory;
@Qualifier("userEntityManager")
public LocalContainerEntityManagerFactoryBean userEntityManagerFactory;
In the main file. Now I have
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'application': Invocation of init method failed; nested exception is java.lang.NullPointerException: Cannot invoke "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.getObject()" because "this.userEntityManagerFactory" is null