I want to insert sql data when I run this datasource configuration but I can't get the data in my test class. It works if I create the data first inside the test class and getting it though.
#Configuration
#EnableJpaRepositories("se.system.repository")
public class DBConfig{
#Bean(name = "hsqldb")
public DataSource InMemoryDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase database = builder
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:se/system/sql/create-db.sql")
.setName("database")
.build();
return database;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.HSQL);
adapter.setShowSql(false);
adapter.setGenerateDdl(true);
return adapter;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(InMemoryDataSource());
factory.setJpaVendorAdapter(jpaVendorAdapter());
factory.setPackagesToScan("se.system.model");
return factory;
}
#Bean
public ResourceDatabasePopulator databasePopulator() {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setSqlScriptEncoding("UTF-8");
populator.addScript(new ClassPathResource("se/system/sql/insert-data.sql"));
return populator;
}
#Bean
public InitializingBean populatorExecutor() {
return () -> DatabasePopulatorUtils.execute(databasePopulator(), InMemoryDataSource());
}
Inside a testclass:
#BeforeClass
public static void setUp() throws ServiceException {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.scan("se.system");
context.refresh();
userService = context.getBean(UserService.class);
System.out.println(userService.getUserById(1L));
}
Spring will look for data.sql as well as data-${platform}.sql files to run. It uses Spring JDBC to do this.
Try renaming your sql file to data.sql and adapt your method accordingly:
populator.addScript(new ClassPathResource("se/system/sql/data.sql"));
instead of
populator.addScript(new ClassPathResource("se/system/sql/insert-data.sql"));
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
Related
In this official example of configuring 2 data sources, they have used embedded DB.
So, with the help of Spring boot's How to documentations, Configure Two DataSources and Using Multiple EntityManagerFactories, I customized that to use MySQL DB.
You can find the complete code under this Github repository. Don't forget to checkout the "two_data_sources_in_separate_files" branch.
But with this modification, "secondDBTransactionManager" is not pointing second_db. Instead, it is inserting data into first_db.
FirstDataSource.java
#Configuration
#EnableJpaRepositories(entityManagerFactoryRef = "firstDBEntityManagerFactory", transactionManagerRef = "firstDBTransactionManager")
public class FirstDataSource {
#Bean
PlatformTransactionManager firstDBTransactionManager(DataSourceProperties firstDataSourceProperties) {
return new JpaTransactionManager(firstDBEntityManagerFactory(firstDataSourceProperties).getObject());
}
#Bean
#Primary
LocalContainerEntityManagerFactoryBean firstDBEntityManagerFactory(DataSourceProperties firstDataSourceProperties) {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(firstDS(firstDataSourceProperties));
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan(FirstDataSource.class.getPackage().getName());
return factoryBean;
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.first")
public DataSourceProperties firstDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.first.configuration")
public HikariDataSource firstDS(DataSourceProperties firstDataSourceProperties) {
return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
SecondDataSource.java
#Configuration
#EnableJpaRepositories(entityManagerFactoryRef = "secondDBEntityManagerFactory", transactionManagerRef = "secondDBTransactionManager")
public class SecondDataSource {
#Bean
PlatformTransactionManager secondDBTransactionManager(DataSourceProperties secondDataSourceProperties) {
return new JpaTransactionManager(secondDBEntityManagerFactory(secondDataSourceProperties).getObject());
}
#Bean
#ConfigurationProperties("app.datasource.second")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("app.datasource.second.configuration")
public HikariDataSource secondDS(
#Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
return secondDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean secondDBEntityManagerFactory(DataSourceProperties secondDataSourceProperties) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(secondDS(secondDataSourceProperties));
factoryBean.setJpaVendorAdapter(vendorAdapter);
factoryBean.setPackagesToScan(SecondDataSource.class.getPackage().getName());
return factoryBean;
}
}
FetchData.java
#Controller // This means that this class is a Controller
#RequestMapping(path = "/demo") // This means URL's start with /demo (after Application path)
public class FetchData {
#Autowired
private TableARepository tableARepository;
#Autowired
private TableCRepository tableCRepository;
#GetMapping(path = "/addDataToFirstDB")
public #ResponseBody void addDataToFirstDB() {
// This returns a JSON or XML with the users
Table_A tableA = new Table_A();
tableA.setTable_a_col_1("test1");
tableA.setTable_a_col_2("test2");
tableA.setTable_a_col_3("test3");
tableA.setTable_a_col_4("test4");
System.out.println("Inserting data into first DB.");
tableARepository.save(tableA);
}
#GetMapping(path = "/addDataToSecondDB")
#Transactional("secondDBTransactionManager")
public #ResponseBody void addDataToSecondDB() {
// This returns a JSON or XML with the users
Table_C tableC = new Table_C();
tableC.setTable_c_col_1("test1");
tableC.setTable_c_col_2("test2");
tableC.setTable_c_col_3("test3");
tableC.setTable_c_col_4("test4");
System.out.println("Inserting data into second DB.");
tableCRepository.save(tableC);
}
}
Observe that, I've used #Transactional("secondDBTransactionManager") for second REST API, "addDataToSecondDB".
It is easy to resolve issue if any Exception is thrown. There is no Exception here but the behavior is not as expected.
I having a configuration problem that cause the error below. I know there are a tons of similar problem but I read a lot of answer that stackoverflow suggest with my title but I don't find the right answer yet.
Description:
Field collaborateurJpaRepository in com...infrastructure.persistence.user.UserRepositoryImpl required a bean of type 'com...infrastructure.persistence.jurisdiction.CollaborateurJpaRepository' that could not be found.
Action:
Consider defining a bean of type 'com...infrastructure.persistence.jurisdiction.CollaborateurJpaRepository' in your configuration.
Before to give some details of the code, I think it could be nice that I describe you brievly the context.
I work on a client project which has 8 maven modules (DDD methods) because at the end of the year 4 modules will be separate in an other server.
Recently we create a second oracle database so I had to create multiple configuration to refer to the good dataSource.
I think the problem is due to bad datasource configuration that block the bean spring instanciation.
We are using spring boot version 1.5.7.release.
First Infrastructure configuration
#Configuration
#ComponentScan(basePackages = "com...jade")
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "jadeEntityManager", transactionManagerRef = "jadeTransactionManager")
#EntityScan(basePackages = { "com...jade", "org.springframework.data.jpa.convert.threeten" })
#Profile({"ARA_JADE_CONF", "LOCAL_JADE_CONF"})
public class InfrastructureConfig {
private final static int JDBC_FETCH_SIZE = 1000;
#Autowired
private Environment environment;
#Bean
public LocalContainerEntityManagerFactoryBean jadeEntityManager() throws SQLException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSourceJade());
em.setPackagesToScan(new String[] {"com...jade"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
#Primary
public OracleDataSource dataSourceJade() throws SQLException {
OracleDataSource dataSource = new OracleDataSource();
dataSource.setUser(environment.getProperty("db.datasource.jade.username"));
dataSource.setPassword(environment.getProperty("db.datasource.jade.password"));
dataSource.setURL(environment.getProperty("db.datasource.jade.url"));
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
return dataSource;
}
#Bean(name="jdbcTemplateJade")
public JdbcTemplate jdbcTemplateJade() throws SQLException {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceJade());
jdbcTemplate.setResultsMapCaseInsensitive(true);
jdbcTemplate.setFetchSize(JDBC_FETCH_SIZE);
return jdbcTemplate;
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException{
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(jadeEntityManager().getObject());
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
} ...
Seconde Infrastructure configuration
#Configuration
#ComponentScan(basePackages = "com.bnpparibas.sit.risk.art")
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "artEntityManager", transactionManagerRef = "artTransactionManager")
#EntityScan(basePackages = { "com...art", "org.springframework.data.jpa.convert.threeten" })
#Profile({"LOCAL_ART_CONF,'ARA_ART_CONF"})
public class ArtInfrastructureConfig {
private final static int JDBC_FETCH_SIZE = 1000;
#Autowired
private Environment environment;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean artEntityManager() throws SQLException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSourceArt());
em.setPackagesToScan(new String[] {"com...art.infrastructure.persistence"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
#Primary
public OracleDataSource dataSourceArt() throws SQLException {
OracleDataSource dataSource = new OracleDataSource();
dataSource.setUser(environment.getProperty("db.datasource.art.username"));
dataSource.setPassword(environment.getProperty("db.datasource.art.password"));
dataSource.setURL(environment.getProperty("db.datasource.art.url"));
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
return dataSource;
}
#Bean
#Primary
public PlatformTransactionManager artTransactionManager()throws SQLException{
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(artEntityManager().getObject());
return transactionManager;
}
#Bean
#Primary
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}...
Interface link to Entity.
#DDD.Repository
#Repository
public interface CollaborateurJpaRepository extends CrudRepository<Collaborateur,String>{
#Query("FROM ART_PARAM.ART_USER u WHERE u.refogUID = :uid")
Collaborateur findCollaborateurById(#Param("uid") String uid);
}
Class that contain the autowired interface to retrieve some data of the second database.
#DDD.RepositoryImpl
#Repository
#Primary
public class UserRepositoryImpl implements UserRepository {
#Autowired
private CollaborateurJpaRepository collaborateurJpaRepository;
#Override
//#Cacheable(cacheNames = CacheConfig.CACHE_JURIDICTION, key = CacheConfig.CACHE_KEY_UTILISATEUR_ID)
#Transactional("entityManagerFactoryArt")
public User retrieveUser(UserId userId) {
if(userId.getValue() == null) {
return null;
} else {
Collaborateur collaborateur = collaborateurJpaRepository.findOne(userId.getValue());
return new User(collaborateur.getRefogUID(),collaborateur.getRefogFirstName(),collaborateur.getRefogLastName(),collaborateur.getProfile(),collaborateur.isActiveUser(),collaborateur.isBankingSecrecy());
}
}
This i my database configuration and app.properties
#Configuration
public class DatabaseConfig {
#Bean
#ConfigurationProperties(prefix = "datasource.two")
public HikariConfig hikariConfig2() {
return new HikariConfig();
}
#Bean
public DataSource dataSource2() {
HikariDataSource hds = new HikariDataSource(new hikariConfig2());
return hds;
}
#Bean
public JdbcTemplate jdbcTemplate2() {
return new JdbcTemplate(dataSource2());
}
#Bean
#ConfigurationProperties(prefix = "datasource.one")
public HikariConfig hikariConfig2() {
return new HikariConfig();
}
#Bean
public DataSource dataSource1() {
HikariDataSource hds = new HikariDataSource(hikariConfig1());
return hds;
}
#Bean
public JdbcTemplate jdbcTemplate1() {
return new JdbcTemplate(dataSource1());
}
}
and
datasource.one.jdbc-url=...
datasource.one.username=...
datasource.one.password=...
datasource.one.driver-class-name=...
datasource.two.jdbcUrl=...
datasource.two.username=...
datasource.two.password=...
datasource.two.driver-class-name=...
How should I create global transaction manager which will support two phase commit. I want to run this app inside docker with Tomcat so no JavaEE.
Also no hibernate. I am using sql-processor with spring-stack:
PS: In some very old article I found this but idk if it rigth sollution
#Bean
public DataSourceTransactionManager tm1() {
DataSourceTransactionManager txm = new DataSourceTransactionManager(refDataSource());
return txm;
}
#Bean
public DataSourceTransactionManager tm2() {
DataSourceTransactionManager txm = new DataSourceTransactionManager(logDataSource());
return txm;
}
#Bean(name = "transactionManager")
#Primary
public PlatformTransactionManager transactionManager() throws Throwable {
return new ChainedTransactionManager(tm1(), tm2());
}
I have a config class:
#SpringBootConfiguration
#ComponentScan(basePackages = "vap")
public class AppConfig {
Logger logger = LoggerFactory.getLogger(this.getClass());
public AppConfig() {
}
#Bean
public ServerRuntime runtime() {
ServerRuntime runtime = ServerRuntime.builder().addConfig("cayenne-project.xml").build();
return runtime;
}
#Bean
public ObjectContext getContext(#Autowired ServerRuntime serverRuntime) {
return serverRuntime.newContext();
}
#Bean(name = "pgDataSource")
public DataSource getDataSource() {
Properties props = new Properties();
props.setProperty("user", "postgres");
props.setProperty("password", "");
PoolConfiguration configuration = new PoolProperties();
configuration.setDbProperties(props);
configuration.setUrl("jdbc:postgresql://localhost/mikro00");
configuration.setDriverClassName("org.postgresql.Driver");
DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(configuration);
return dataSource;
}
#Bean(name = "hsqldbDataSource")
public DataSource getHSQLDataSource() {
Properties props = new Properties();
props.setProperty("user", "sa");
props.setProperty("password", "");
PoolConfiguration configuration = new PoolProperties();
configuration.setDbProperties(props);
configuration.setUrl("jdbc:h2:file:./outbase");
configuration.setDriverClassName("org.h2.Driver");
DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource(configuration);
return dataSource;
}
}
my PGConfig.java
#Configuration
#EnableTransactionManagement
public class PGConfig {
#Primary
#Bean(name = "entityManagerFactoryPG")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier(value = "pgDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean vap = builder.dataSource(dataSource)
.packages("vap")
.build();
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
vap.setJpaVendorAdapter(jpaVendorAdapter);
return vap;
}
#Primary
#Bean(name = "transactionManagerPG")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
My H2Config.java
#Configuration
#EnableTransactionManagement
public class H2Config {
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, #Qualifier(value = "hsqldbDataSource") DataSource dataSource){
LocalContainerEntityManagerFactoryBean vap = builder.dataSource(dataSource)
.packages("vap")
.build();
JpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
vap.setJpaVendorAdapter(jpaVendorAdapter);
return vap;
}
#Primary
#Bean(name = "transactionManagerH2")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
KlientRepository
#Repository
public interface KlientRepository extends CrudRepository<Klient,Integer> {
}
How can I read Klient from one repository and write to another. I need to read from PG, work with data and save to h2. I can't find how two object of repository with different datasource, or simply create repository object with concrete datasource
You have pretty much everything out there in your code, you only need to do a bit of fine tuning thats all
Create two configuration classes with bean declaration for Datasource, EntityManagerFactory and TransactionManager
Mark one of the two as primary
Create two model classes(one for each database model)
Create two Repository classes in two different package**(very Important)**
In your service class Autowire both Repositories, read from one DB, manipulate and save to other.
Only thing missing in your code is you need to tell Spring which Repository class should use which EntityManager /Datasource(Since you have two). This can be done by Annotation #EnableJpaRepositories(basepackages=...). Use this annotation on each configuration classes, with basePackages indicating your repository classes
I'm working with Spring and in it spring-data-jpa:1.7.0.RELEASE and hibernate-jpa-2.1-api:1.0.0.Final. My database is a MySQL. In a integration test, when I save an entity, the entityRepository.save() method does not return a update version of it (with the auto-incremented id populated for example), thus I can see it is in the database.
Here's my configuration class:
//#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "core.repository")
#Configuration
#PropertySource("classpath:application.properties")
public class JPAConfiguration {
#Autowired
private Environment env;
private
#Value("${db.driverClassName}")
String driverClassName;
private
#Value("${db.url}")
String url;
private
#Value("${db.username}")
String user;
private
#Value("${db.password}")
String password;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setDatabasePlatform(env.getProperty("hibernate.dialect"));
final LocalContainerEntityManagerFactoryBean em =
new LocalContainerEntityManagerFactoryBean();
em.setJpaVendorAdapter(vendorAdapter);
em.setPackagesToScan(new String[]{"core.persistence"});
em.setDataSource(dataSource());
em.afterPropertiesSet();
return em;
}
#Bean
public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public JpaTransactionManager transactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Here's my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {JPAConfiguration.class})
//#Transactional
//#TransactionConfiguration(defaultRollback = true)
public class AgencyRepositoryIntegrationTests {
#Autowired
AgencyRepository agencyRepository;
#Test
public void testThatAgencyIsSavedIntoRepoWorks() throws Exception {
Agency c = DomainTestData.getAgency();
Agency d = agencyRepository.save(c);
// here d equals to c but both have id 0.
Collection<Agency> results = Lists.newArrayList(agencyRepository.findAll());
//here results != null and it does contain the agency object.
assertNotNull(results);
}
}
I end up commenting the Transactional annotations (both in the test and in the configuration class) due to research other questions in stackoverflow, but it did not work.
Thanks
Try to uncomment your #Transactional annotation and run. I can't see any visible problem in your snippet but there is something inside me (and it's not hunger) saying that your transaction demarcation is not right. I had similar problems like this before and it was just #transaction annotations missing or transaction misconfiguration.
Use saveAndFlush method to make sure that the entity is in the database.
Make sure to add #Transactional to your methods if a NoTransactionException appears.