Spring Data JPA in two repositories for databases - mysql

I want to use spring data jpa repository. And I have to connect to 2 databases. I already found so many similar questions. But most of answers are using Entity manager instead of repository.
For example
#persistenceContext(unitname = "example")
Entitymanager em;
But I want to use repository of spring data jpa. How can I configure in applicationContext.xml?
My 2 databases are MySQL and one is local another is remote server.

You can separate those repositories in different packages. Then its possible to create two DB's configurations with different enity manager factory and transaction manager.
For example first config:
#Configuration
#EnableJpaRepositories(basePackages = "com.firstpackage",
entityManagerFactoryRef = "entityManagerFactoryDb1",
transactionManagerRef = "transactionManagerDb1")
public class DB1Config {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryDb1() {
...
}
public JpaTransactionManager transactionManagerDb1() {
...
}
public DataSource dataSourceDb11() {
...
}
Second config would be similar.
You didnt put many details in question, but in case you need to switch databases for same repositories ( for example for different locales ) you can use AbstractRoutingDataSource class which will define determineCurrentLookupKey method.

Related

Why the second database not created when multiple datasources in Springboot application?

I want to use two (Mysql) databases in my Springboot application. Following the instructions I use the following configuration
app.properties
spring.datasource.url = jdbc:mysql://localhost:3306/db1?createDatabaseIfNotExist=true&autoReconnect=true
spring.datasource.username = root
spring.datasource.password = password
spring.seconddatasource.url = jdbc:mysql://localhost:3306/db2?createDatabaseIfNotExist=true&autoReconnect=true
spring.seconddatasource.username = root
spring.seconddatasource.password = password
DataSourceConfig.java
#Configuration
public class DataSourceConfig {
#Bean("dataSource")
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create()
.type(DriverManagerDataSource.class)
.build();
}
#Bean("secondDataSource")
#ConfigurationProperties(prefix = "spring.seconddatasource")
public DataSource secondDataSource() {
return DataSourceBuilder.create()
.type(DriverManagerDataSource.class)
.build();
}
}
The application starts without errors but only the first database (or whichever datasource bean is marked as primary) gets created. Why not the second?
EDIT:
Once I create the second database manually, the application connects to both of them just fine. It is the automatic creation of the non-primary database only that is causing the problems.
Because you're using #ConfigurationProperties wrong. The annotation most certainly does not point a bean to the relevant configuration. The first DB gets created because, well, spring.datasource.* are actually standard Spring Boot properties.
If you wish to create two data sources, at the very least you'll need to set the appropriate properties (url, password) on the second one yourself. You may inject your custom properties (spring.seconddatasource.*) into the configuration class using #Value, of course.

Is it possible to use JDBC Template and JDBC MySQL at the same time?

I’m currently building a simple CRUD based app, and I’m done with the basic stuff, like creating a form with a title, date, description etc, editing or deleting a post etc. So now I’m trying to add an image upload function as well. (I’m using Windows 10 as my OS)
At this moment, I’m studying a tutorial of the following URL
https://www.callicoder.com/spring-boot-file-upload-download-jpa-hibernate-mysql-database-example/
, and when I take a look at
Configuring the Database and Multipart File properties
section, it explains what statements are needed to configure the database and multipart file properties, but when I added the tutorial page’s sample it caused conflict.
The below is how my application.properties file looks like.
(The line “## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties” and after that is the part that I copied and pasted from the tutorial, and above that is the original code before adding the tutorial’s)
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.h2.console.enabled=true
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root
spring.datasource.password= callicoder
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
## Hibernate Logging
logging.level.org.hibernate.SQL= DEBUG
## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB
The part that’s causing the conflict is the following.
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root
I assume that the reason for the conflict is that I’m trying to use JDBC H2 database and JDBC MySQL at the same time. At first I thought that commenting out my original configuration like below would solve the problem,
#spring.datasource.url=jdbc:h2:mem:test
#spring.datasource.driverClassName=org.h2.Driver
#spring.datasource.username=sa
#spring.h2.console.enabled=true
, but after this I couldn’t run the program probably because there is a part where I use JDBC Templace like below.
[ReportDaoImpl.java]
package com.example.demo.repository;
import java.sql.Timestamp;
#Repository
public class ReportDaoImpl implements ReportDao {
private final JdbcTemplate jdbcTemplate;
#Autowired
public ReportDaoImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Override
public List<Report> findAll() {
String sql = "SELECT report_id, title, threat_level, report_date, description, img_path, "
+ "user.user_id, user_name FROM report "
+ "INNER JOIN user ON report.user_id = user.user_id";
List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql);
……
My biggest question is, how can I integrate the tutorial’s “upload an image” function to my basic CRUD app without causing a conflict in configuration?
Should I give up using JDBC H2 Database and JDBC Template and use something else that is compatible with the JDBC MySQL part that I’m taking from the tutorial? In other words, in order to integrate the tutorial’s image upload function, should I fundamentally restructure my code in ReportDaoImpl.java file (and maybe even other files as well?), or would there be a simple way to resolve the configuration conflict?
You don't have to give away any of your Databases if you intend to use them for absolutely necessary reasons. Spring doesn't DataSourceAutoConfiguration can't differentiate between the two configs in the property file for the simple fact that the property file is a key-value pair map. And hence it would override the configs.
The easiest way to resolve this is:
Create separate keys for different datasources as under:
## Your Primary Data Source
spring.datasource-primary.url=jdbc:h2:mem:test
spring.datasource-primary.driverClassName=org.h2.Driver
spring.datasource-primary.username=sa
spring.h2.console.enabled=true
## Your Secondary Data Source
spring.datasource-secondary.url= jdbc:mysql://localhost:3306/file_demo?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource-secondary.username= root
spring.datasource-secondary.password= callicoder
Add a DataSourceConfig as under
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
/**
* Configures the Spring-managed resources for Common Services/Utils.
*/
#Configuration
public class DataSourceConfig {
#Autowired
Environment env;
/**
* Primary DataSource (Meaning the one that is your parent transaction manager)
*/
#Bean
#Primary
public DataSource h2DataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource-primary.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource-primary.url"));
dataSource.setUsername(env.getProperty("spring.datasource-primary.username"));
dataSource.setPassword(env.getProperty("spring.datasource-primary.password"));
return dataSource;
}
/**
* #usage Autowire this in your JPA Repositories using
* #Autowired
* JdbcTemplate h2JdbcTemplate;
*/
#Bean
public JdbcTemplate h2JdbcTemplate() {
return new JdbcTemplate(h2DataSource());
}
/**
* Secondary DataSource (Meaning the one that can cause the parent transaction to roll-back on exception)
*/
#Bean
public DataSource mysqlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource-secondary.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource-secondary.url"));
dataSource.setUsername(env.getProperty("spring.datasource-primary.username"));
dataSource.setPassword(env.getProperty("spring.datasource-secondary.password"));
return dataSource;
}
/**
* #usage Autowire this in your JPA Repositories using
* #Autowired
* JdbcTemplate mysqlJdbcTemplate;
*/
#Bean
public JdbcTemplate mysqlJdbcTemplate() {
return new JdbcTemplate(mysqlDataSource());
}
}
Use in right JdbcTemplate in your repository classes
package com.example.demo.repository;
import java.sql.Timestamp;
#Repository
public class ReportDaoImpl implements ReportDao {
//Note the JdbcTemplate variable name here
private final JdbcTemplate myslJdbcTemplate;
#Autowired
//Note the JdbcTemplate variable name here
public ReportDaoImpl(JdbcTemplate myslJdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Override
public List<Report> findAll() {
String sql = "SELECT report_id, title, threat_level, report_date, description, img_path, "
+ "user.user_id, user_name FROM report "
+ "INNER JOIN user ON report.user_id = user.user_id";
List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql);
……
You would need to update the repective JdbcTemplate for all the respective repository classes.
Cheers and happy coding!
You cannot define the same key multiple times in application.properties , one will override the other. That means if you need to use multiple datasources (for MySQL and H2), you cannot relies on the spring.datasource.xxx in application.properties. Instead, define the two DataSource explicitly by yourself. See the official docs for an example.
Also , JdbcTemplate will only be configured if :
Only one DataSource defined
If multiple DataSource are defined but only one DataSource is marked as #Primary, and it will configured just for this #Primary DataSource.
So that means after you define multiple DataSource , you have to mark the H2 one as #Primary such that the JdbcTemplate will be auto configured for it only to ensure that your existing JDBCTempalte related codes still interact with H2 but not MySQL.
By the way, there is no advantages to use multiple database for a simple CRUD app. You will encounter issues if you want to have a transaction which cover the data from multiple databases. I suggest you only choose one for such a simple app.
(Also see my related answer for more detail)

DataSourceInitializer is not working on Spring boot 1.2

I am new to Spring boot.I want to add some sql while database is creating like seed data.
#Value("classpath:com/foo/sql/db-test-data.sql")
private Resource dataScript;
#Bean
public DataSourceInitializer dataSourceInitializer(final DataSource dataSource) {
final DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource);
initializer.setDatabasePopulator(databasePopulator());
return initializer;
}
private DatabasePopulator databasePopulator() {
final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScript(dataScript);
return populator;
}
props.put("hibernate.query.substitutions", "true 1, false 0");
props.put("hibernate.hbm2ddl.auto", "create-drop");
props.put("hibernate.show_sql", "false");
props.put("hibernate.format_sql", "true");
I have perform this action.But it not working on spring boot.Can any one help me.
Sometimes spring-boot gets more in the way than it helps; IMHO this is especially so with web applications.
What you can do to get around this is to rename the bean that you define.
#Bean("springBootPleaseStopTellingMeHowYouThinkDataSourceInitializer")
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
// build it.
}
Now, to turn off the built in bit that looks for data.sql in application.properties
spring.datasource.initialize=false
There, now boot is booted out of the way.
You can take advantage of Spring Boot database initialization capabilities. The simplest way is to place a "data.sql" file in the root of the classpath. So you just need to:
Change your sql file name to "data.sql".
Place it in "src/main/resources".
Spring Boot will automatically pick up the file and use it to initialize the database on startup.
You can check the documentation if you need to customize the file name, location, etc.

Spring boot jpa + cassandra + mysql

I want to use both cassandra and MySQL in my project (Some objects will be saved in cassandra, some other will be save in MySQL. My issue is how to config it.
Currently, my configuration is
#SpringBootApplication
#EnableCassandraRepositories(basePackages = { "com.example.cassandra.repository" })
#PropertySource(value = { "classpath:cassandra.properties" })
#EnableJpaRepositories(basePackages = { "com.example.mysql.repository" })
#EntityScan(basePackages = "com.example.mysql.model")
#EnableTransactionManagement
public class Application extends AbstractCqlTemplateConfiguration {}
But, I can not #Autowired an Repository from MySQL dialect, as well as can not #Autowired SessionFactory. Please help me to config them.
Could you show more error log. But i think you should separate them into two different sub-projects. It can make your code more clean and maintainable.

How to properly set-up Spring Boot & Hibernate using InteliJ?

As the title suggests I am building a web application using Spring Boot and Hibernate for my data access layer and the development is done on InteliJ IDEA 14.1.2.
My Knowledge
This is my first time using Spring Boot, Hibernate and InteliJ. I have built a few small apps to test Spring Boot and Hibernate, but the complexity difference between those and the one I am building now is a bit bigger.
Environment
Regarding my environment, in case it matters, I am running Windows 7 SP1 64bit, MySQL server 5.6.17, InteliJ 14.1.2 and Ubuntu Server 14.04 on a VirtualBox 4.3.26 VM hosting a Redis 3.0.1 server.
Purpose
The purpose of using the above technologies at this point in time is the storage and retrieval of different entities to a MySQL database (Redis is used only for session externalization and sharing among app instances). In other words, I am building my data access layer.
Database
My complete database schema can be found here:
https://dl.dropboxusercontent.com/u/49544122/so/DB.pdf
Source
My Spring Boot application is the following:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import se.domain.cvs.abstraction.dataaccess.AccountRepository;
import se.domain.cvs.domain.AccountEntity;
#SpringBootApplication
#EnableRedisHttpSession
public class Application implements CommandLineRunner {
#Autowired
AccountRepository repository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... strings) throws Exception {
System.out.println("=======================================================");
AccountEntity account = repository.findByEmail("r.franklin#companya.se");
System.out.println("My name is " + account.getFirstName() + " " + account.getLastName());
System.out.println("=======================================================");
}
}
I am using CommandLineRunner interface just to test the bare data access layer without introducing REST endpoints yet.
My configuration is the following in YAML format:
...
# MySQL Database Configuration
spring.datasource:
url: jdbc:mysql://localhost:3306/cvs
username: cvs
password: cvs
driverClassName: com.mysql.jdbc.Driver
spring.jpa:
database: MYSQL
show-sql: true
hibernate.ddl-auto: validate
hibernate.naming-strategy: org.hibernate.cfg.DefaultNamingStrategy
properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect
...
The JPA entities are automatically generated with InteliJ and that is where the problems begin. Let's take for example the OrderEntity below (for the sake of brevity I omit some code):
...
#Entity
#Table(name = "order", schema = "", catalog = "cvs")
public class OrderEntity {
...
private int invoiceId;
...
private InvoiceEntity invoiceByInvoiceId;
...
#Basic
#Column(name = "InvoiceID", nullable = false, insertable = false, updatable = false)
public int getInvoiceId() {
return invoiceId;
}
public void setInvoiceId(int invoiceId) {
this.invoiceId = invoiceId;
}
...
#ManyToOne
#JoinColumn(name = "InvoiceID", referencedColumnName = "InvoiceID", nullable = false)
public InvoiceEntity getInvoiceByInvoiceId() {
return invoiceByInvoiceId;
}
public void setInvoiceByInvoiceId(InvoiceEntity invoiceByInvoiceId) {
this.invoiceByInvoiceId = invoiceByInvoiceId;
}
...
}
When trying to run the Spring Boot application I get the following error:
org.hibernate.MappingException: Repeated column in mapping for entity: OrderEntity column: invoiceId (should be mapped with insert="false" update="false")
After doing a little bit of research, I guess the problem is that the invoiceID now has two ways to be set, one through the setInvoiceID() setter and one through the InvoiceEntity object itself that the OrderEntity relates to, which could lead to an inconsistent state. As another user here puts it,
You would do that when the responsibility of creating/udpating the related entity in question isn't in the current entity.
See related post here: Please explain about: insertable=false, updatable=false
Setting the proposed values of the corresponding field (insertable and updateable) to false fixes the error.
My question here is why is this generated the wrong way? My change fixed the error, but I want to make sure that there is no errors in my SQL that lead InteliJ to generate this the wrong way. The complete SQL script can be found here http://pastebin.com/aDguqR1N.
Additionally, when generating the Entities, InteliJ requires a Hibernate config file which I guess Spring Boot generates on its own somewhere else (or uses Java based configuration). Whether I leave it there or delete it, it doesn't seem to affect the app at all. I guess the order taken by SB to read properties overrides it. Is it OK that I just remove it?
Thank you very much for your time and help in advance and sorry for this long post! :)
my advice is to let Spring/Hibernate let generate your db schema for you ( everything including foreign keys and constraints can be generated by Spring.
For me the folloeing approach worked:
in the parent entity(in my case the TblUser):
#OneToMany(targetEntity=TblTracks.class,fetch=FetchType.EAGER,cascade=CascadeType.ALL,mappedBy="tbluser")
private List<TblTracks> tbltracks= new ArrayList<TblTracks>();
where mappedBy points to the Tbluser Entity (private TblUser tbluser) of the child Entity
and in the child entity (in my case TblTracks) like
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="idTblUser",nullable=false)
private TblUser tbluser;