Spring Boot 应用系列 1 -- Spring Boot 2 整合Spring Data JPA和Druid,双数据源
最近Team开始尝试使用Spring Boot + Spring Data JPA作为数据层的解决方案,在网上逛了几圈之后发现大家并不待见JPA,理由是(1)MyBatis简单直观够用,(2)以Hibernate为底层的Spring Data JPA复杂且性能一般。
但是当我们来到Spring Boot的世界后发现,相较于Spring Data JPA,MyBatis对Spring Boot的支持有限,Spring Data JPA与Spring Boot结合可以让dao变得非常简单,比如(1)JPA自带分页对象,无需设置插件;(2)一个空接口搞定所有基本CRUD。
本着虚心学习的态度,我决定将Spring Boot、Spring Data JPA和Druid三者整合在一起,并分别对SQL Server和MySQL进行支持,希望本文能够帮助到需要相关技术的同学。
1. 程序和版本
Spring Boot 2.0.4
mssql-jdbc 6.2.2.jre8
mysql-connector-java 5.1.46
druid-spring-boot-starter 1.1.10
2. properties配置文件
我们把主程序配置文件application.properties和数据库配置文件分开,这样可使application.properties不至于臃肿。
(1) application.properties
1 server.port=9006 2 spring.application.name=spring-data-jpa 3 4 #Serialize JPA entity to Json string. 5 spring.jackson.serialization.fail-on-empty-beans=false
第5行的作用是避免com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer,该配置只对MSSQL数据源有效。
(2) db.properties
1 #Data source 1 2 db1.sqlserver.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver 3 db1.sqlserver.url=${DB1_URL:jdbc:sqlserver://127.0.0.1:1433;DatabaseName=MyTestDb1} 4 db1.sqlserver.username=${DB1_UID:tester} 5 db1.sqlserver.password=${DB1_PWD:tester} 6 db1.sqlserver.initial-size=1 7 db1.sqlserver.min-idle=1 8 db1.sqlserver.max-active=20 9 db1.sqlserver.max-wait=60000 10 db1.sqlserver.time-between-eviction-runs-millis=60000 11 db1.sqlserver.min-evictable-idle-time-millis=300000 12 db1.sqlserver.validation-query=select 1 13 db1.sqlserver.test-on-borrow=true 14 db1.sqlserver.test-While-Idle=true 15 db1.sqlserver.test-on-return=false 16 db1.sqlserver.pool-prepared-statements=false 17 db1.sqlserver.max-pool-prepared-statement-per-connection-size=20 18 19 db1.sqlserver.filter.stat.enabled=true 20 db1.sqlserver.filter.stat.db-type=mssql 21 db1.sqlserver.filter.stat.log-slow-sql=true 22 db1.sqlserver.filter.stat.slow-sql-millis=2000 23 24 db1.sqlserver.jpa.hibernate.dialect=org.hibernate.dialect.SQLServerDialect 25 db1.sqlserver.jpa.hibernate.show_sql=true 26 db1.sqlserver.jpa.hibernate.format_sql=true 27 28 #Data source 2 29 db2.mysql.driver-class-name=com.mysql.jdbc.Driver 30 db2.mysql.url=${DB2_URL:jdbc:mysql://127.0.0.1:3306/Test}?useUnicode=true&useSSL=false 31 db2.mysql.username=${DB2_UID:tester} 32 db2.mysql.password=${DB2_PWD:tester} 33 db2.mysql.initial-size=1 34 db2.mysql.min-idle=1 35 db2.mysql.max-active=20 36 db2.mysql.max-wait=60000 37 db2.mysql.time-between-eviction-runs-millis=60000 38 db2.mysql.min-evictable-idle-time-millis=300000 39 db2.mysql.validation-query=select 1 40 db2.mysql.test-on-borrow=true 41 db2.mysql.test-While-Idle=true 42 db2.mysql.test-on-return=false 43 db2.mysql.pool-prepared-statements=false 44 db2.mysql.max-pool-prepared-statement-per-connection-size=20 45 46 db2.mysql.filter.stat.enabled=true 47 db2.mysql.filter.stat.db-type=mysql 48 db2.mysql.filter.stat.log-slow-sql=true 49 db2.mysql.filter.stat.slow-sql-millis=2000 50 51 db2.mysql.jpa.hibernate.dialect=org.hibernate.dialect.MySQLDialect 52 db2.mysql.jpa.hibernate.show_sql=true 53 db2.mysql.jpa.hibernate.format_sql=true 54 db2.mysql.jpa.hibernate.enable_lazy_load_no_trans=true
该配置文件可分为三部分:一是JPA的数据源基本信息配置(行5之前);二是JPA的数据库连接池配置(行6-行17);三是Druid连接池的特殊配置(行19-行22);四是自定义配置(行24-行26)。
需要注意行54的配置,加这一行是为了解决由Hibernate懒加载引起的异常org.hibernate.LazyInitializationException: could not initialize proxy [devutility.test.database.springdatajpa.dao.mysql.entity.Customer#100000123] - no Session
但是让enable_lazy_load_no_trans=true会带来一定的性能问题,具体参考https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/
此外,解决org.hibernate.LazyInitializationException异常还有另外一种方法,在每个Entity类型上添加@Proxy(lazy = false)注解,经测试有效。
3. Java Config
为便于管理,每个数据源一个配置类,此处只列出一个数据源:
1 import java.util.Properties; 2 3 import javax.sql.DataSource; 4 5 import org.springframework.boot.context.properties.ConfigurationProperties; 6 import org.springframework.context.annotation.Bean; 7 import org.springframework.context.annotation.Configuration; 8 import org.springframework.context.annotation.Primary; 9 import org.springframework.context.annotation.PropertySource; 10 import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 11 import org.springframework.orm.jpa.JpaTransactionManager; 12 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 13 import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 14 import org.springframework.transaction.PlatformTransactionManager; 15 16 import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; 17 18 import devutility.internal.util.PropertiesUtils; 19 20 @Configuration 21 @PropertySource("classpath:db.properties") 22 @EnableJpaRepositories(basePackages = "devutility.test.database.springdatajpa.dao.mssql", entityManagerFactoryRef = "entityManagerFactory1", transactionManagerRef = "transactionManager1") 23 public class DataSource1Configuration { 24 @Primary 25 @Bean 26 @ConfigurationProperties("db1.sqlserver") 27 public DataSource dataSource1() { 28 return DruidDataSourceBuilder.create().build(); 29 } 30 31 @Bean 32 @ConfigurationProperties("db1.sqlserver.jpa") 33 public Properties jpaProperties1() { 34 return new Properties(); 35 } 36 37 @Primary 38 @Bean 39 public LocalContainerEntityManagerFactoryBean entityManagerFactory1() { 40 LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); 41 localContainerEntityManagerFactoryBean.setDataSource(dataSource1()); 42 localContainerEntityManagerFactoryBean.setPackagesToScan(new String[] { "devutility.test.database.springdatajpa.dao.mssql.entity" }); 43 localContainerEntityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter()); 44 localContainerEntityManagerFactoryBean.setJpaPropertyMap(PropertiesUtils.toMap(jpaProperties1())); 45 return localContainerEntityManagerFactoryBean; 46 } 47 48 @Bean 49 public PlatformTransactionManager transactionManager1() { 50 JpaTransactionManager transactionManager = new JpaTransactionManager(); 51 transactionManager.setEntityManagerFactory(entityManagerFactory1().getObject()); 52 return transactionManager; 53 } 54 }
4. Druid控制台页面配置
Druid的详细配置见Druid官网
如果你不想对Druid控制台的访问加以限制可以忽略此节,如果你希望通过用户名和密码访问Druid控制台,有如下两种配置方式:
(1)Java Config
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter;@Configuration public class DruidConfiguration {@Beanpublic ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");servletRegistrationBean.addInitParameter("loginUsername", "admin");servletRegistrationBean.addInitParameter("loginPassword", "admin");servletRegistrationBean.addInitParameter("resetEnable", "false");return servletRegistrationBean;}@Beanpublic FilterRegistrationBean<WebStatFilter> druidStatFilter() {FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());filterRegistrationBean.setName("DruidWebStatFilter");filterRegistrationBean.addUrlPatterns("/*");filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");return filterRegistrationBean;} }
(2). 在application.properties文件中添加
#Configuration for druid spring.datasource.druid.stat-view-servlet.enabled=true spring.datasource.druid.stat-view-servlet.url-pattern=/druid/* spring.datasource.druid.stat-view-servlet.login-username=admin spring.datasource.druid.stat-view-servlet.login-password=admin
5. 应用
配置好之后就该实现CRUD的基本功能了:
(1) 定义一个实体类Customer
import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table;@Entity @Table(name = "Customer") public class Customer extends BaseEntity {@Idprivate long id;@Column(name = "Name1")private String name;@Column(name = "Address1")private String address;private String city;private int state;private int zip;private String phone;private String email;
这里需要注意以下几点:
a. 所有JPA的实体类都需要有@Entity的注解;
b. @Table注解可选,如果不设置则表名=类名,如果表名和类名不一致则需要配置;
c. @Column注解可选,用于表中字段名和实体类的属性不一致的情况;
d: 可在拥有@Id字段上添加@GeneratedValue注解用于生成主键。
(2) Dao层
a. 对于每一个表,只需要定义一个简单的接口并继承JpaRepository<T, ID>即可实现基本的CRUD还有分页操作:
package devutility.test.database.springdatajpa.dao.mysql;import org.springframework.data.jpa.repository.JpaRepository;import devutility.test.database.springdatajpa.dao.mysql.entity.Customer;public interface CustomerRepository extends JpaRepository<Customer, Long> {}
b. 假设你的实体类是通过联表查询得到的,或者对于一个单表来说基本的CRUD无法满足你的需求,你可以通过使用@Query注解来手写SQL语句实现,下面我们来演示一下这种情况:
首先定义一个实体类SimpleCustomer,该实体类只包含Customer的部分字段。
package devutility.test.database.springdatajpa.dao.mysql.entity;import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table;@Entity @Table(name = "Customer") public class SimpleCustomer {@Idprivate long id;private String 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;} }
然后我们再定义SimpleCustomer对应的Repository:
package devutility.test.database.springdatajpa.dao.mysql;import java.util.Date; import java.util.List;import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query;import devutility.test.database.springdatajpa.dao.mysql.entity.SimpleCustomer;public interface SimpleCustomerRepository extends JpaRepository<SimpleCustomer, Long> {@Query(value = "select ID, Name1 Name, Address1 Address, Created from Customer where Created > ?1 and Name1 is not null order by Created desc limit ?2, ?3", nativeQuery = true)List<SimpleCustomer> paging(Date startDate, int skip, int pageSize); }
在SimpleCustomerRepository中,我们定义了一个接口paging,用来进行分页查询。注意,一定要有nativeQuery = true,否则报错。
(3) 应用层
接下来就是怎样使用上面定义的Repository了:
import java.text.ParseException; import java.util.Date; import java.util.List;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;import devutility.internal.models.OperationResult; import devutility.internal.text.format.DateFormatUtils; import devutility.test.database.springdatajpa.dao.mysql.CustomerRepository; import devutility.test.database.springdatajpa.dao.mysql.SimpleCustomerRepository; import devutility.test.database.springdatajpa.dao.mysql.entity.Customer; import devutility.test.database.springdatajpa.dao.mysql.entity.SimpleCustomer;@RestController @RequestMapping("/mysql") public class MySqlController {private int pageSize = 10;@Autowiredprivate CustomerRepository customerRepository;@Autowiredprivate SimpleCustomerRepository simpleCustomerRepository;@RequestMapping("/customer")public Customer findCustomer(String id) {return customerRepository.getOne(id);}@RequestMapping("/update-customer")public OperationResult updateCustomer(String id) {OperationResult result = new OperationResult();Customer customer = customerRepository.getOne(id);if (customer == null) {result.setErrorMessage(String.format("Customer with id %d not found!", id));return result;}customer.setName("Test-Customer");Customer updatedCustomer = customerRepository.save(customer);result.setData(updatedCustomer);return result;}@RequestMapping("/paging-customers")public List<Customer> pagingCustomers(int page) {Pageable pageable = PageRequest.of(page, pageSize, Sort.by(Direction.DESC, "Created"));Page<Customer> customerPage = customerRepository.findAll(pageable);System.out.println(String.format("TotalElements: %d", customerPage.getTotalElements()));System.out.println(String.format("TotalPages: %d", customerPage.getTotalPages()));return customerPage.getContent();}@RequestMapping("/paging-simple-customers")public List<SimpleCustomer> pagingSimpleCustomers(int page) throws ParseException {Date startDate = DateFormatUtils.parse("2018-01-01", "yyyy-MM-dd");return simpleCustomerRepository.paging(startDate, (page - 1) * pageSize, pageSize);} }
除此之外,save方法也用于新增,delete方法用于删除,不再赘述。
Demo代码
转载于:https://www.cnblogs.com/eagle6688/p/9557620.html
Spring Boot 应用系列 1 -- Spring Boot 2 整合Spring Data JPA和Druid,双数据源相关推荐
- Spring Boot干货系列:(十二)Spring Boot使用单元测试 | 嘟嘟独立博客
原文地址 2017-12-28 开启阅读模式 Spring Boot干货系列:(十二)Spring Boot使用单元测试 Spring Boot干货系列 Spring Boot 前言 这次来介绍下Sp ...
- Spring Boot入门系列(十六)整合pagehelper,一秒实现分页功能!
之前讲了Springboot整合Mybatis,然后介绍了如何自动生成pojo实体类.mapper类和对应的mapper.xml 文件,并实现最基本的增删改查功能.接下来要说一说Mybatis 的分页 ...
- Spring Boot 入门系列(二十三)整合Mybatis,实现多数据源配置!
d之前介绍了Spring Boot 整合mybatis 使用注解方式配置的方式实现增删改查以及一些复杂自定义的sql 语句 .想必大家对spring boot 项目中,如何使用mybatis 有了一定 ...
- Spring Boot入门系列(十八)整合mybatis,使用注解的方式实现增删改查
之前介绍了Spring Boot 整合mybatis 使用xml配置的方式实现增删改查,还介绍了自定义mapper 实现复杂多表关联查询.虽然目前 mybatis 使用xml 配置的方式 已经极大减轻 ...
- Spring Cloud Alibaba系列四:集成 seata 实现分布式事务
文章目录 Spring Cloud Alibaba系列四:集成 seata 实现分布式事务 前言 Seata 是什么? Seata 术语 安装 seata 1.创建 seata 数据库,并添加对应的表 ...
- Portal-Basic Java Web 应用开发框架:应用篇(十一) —— 整合 Spring
Portal-Basic Java Web应用开发框架(简称 Portal-Basic)是一套功能完备的高性能Full-Stack Web应用开发框架,内置稳定高效的MVC基础架构和DAO框架(已内置 ...
- 【Spring Data JPA自学笔记二】初识Spring Data JPA
文章目录 Spring Data JPA是什么? Spring Data JPA的配置 配置pom.xml 配置applicationContext.xml Spring Data JPA的使用 Sp ...
- Spring Boot干货系列:(六)静态资源和拦截器处理 | 掘金技术征文
原本地址:Spring Boot干货系列:(六)静态资源和拦截器处理 博客地址:tengj.top/ 前言 本章我们来介绍下SpringBoot对静态资源的支持以及很重要的一个类WebMvcConfi ...
- Spring Boot实战系列《六》:人事管理系统的登录设计
Spring Boot实战系列<六>:人事管理系统的登录设计 Spring Boot实战系列<六>:人事管理系统的登录设计 1.前言 在上一篇中教大家在IEDA或者eclips ...
最新文章
- 网络营销专员浅析企业网站网络营销的多个优势!
- base64 长度补全
- Scheme 语言概要
- MyBatis查询结果resultType返回值类型详细介绍
- 第二十六讲:基础一开放封闭原则
- c++ 转bcd码_8421BCD码转换为十进制
- Coursera课程 Competitive Strategy内容简介
- 《大话设计模式》第29章-OOTV杯超级模式大赛—模式总结(五)
- 如何做一名了不起的开发人员?
- zabbix 监控项自动发现过滤_Zabbix使用javascript+jsonpath预处理动态生成监控项
- python中质数的表达方式_python求质数的3种方法
- web前端教程,详解引入CSS的4种方式
- 正交试验设计例题及答案_正交试验设计与数理统计作业.doc
- 计算机八进制 算法视频,八进制算法
- 二、GAMIT解算之数据准备
- Oracle用户、身份、数据类型、和mysql的区别
- 经纬财富:十堰炒白银有哪些技巧
- 正弦定理、余弦定理及解三角形
- 境外服务器停机:原因、影响以及如何预防
- 【电机原理与拖动基础】Unit 1 直流电机(你还不知道电机是怎么一回事吗?那就快来看一看吧!)
热门文章
- 【原理+实战+视频+源码】手撸SpringBoot缓存系统
- 【2021年度训练联盟热身训练赛第二场】Tip to be Palindrome(python)
- python【力扣LeetCode算法题库】1103- 分糖果 II
- 【Network Security!】认识进程与端口
- ORACLE not available如何解决
- mysql双机数据热备份_mysql 双机交互热备份
- 运筹学两阶段法编程c语言,运筹学上机实验 - 单纯形方法的两阶段法
- 组态王与c语言混合编程6,亚控 组态王嵌入版6.1
- 网络推广——符合网站现状的优化方案才是最适合进行网络推广的方案
- yota3墨水屏设置_国产墨水屏“手写平板”评测,10.3英寸大屏,支持无纸化办公...