最近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,双数据源相关推荐

  1. Spring Boot干货系列:(十二)Spring Boot使用单元测试 | 嘟嘟独立博客

    原文地址 2017-12-28 开启阅读模式 Spring Boot干货系列:(十二)Spring Boot使用单元测试 Spring Boot干货系列 Spring Boot 前言 这次来介绍下Sp ...

  2. Spring Boot入门系列(十六)整合pagehelper,一秒实现分页功能!

    之前讲了Springboot整合Mybatis,然后介绍了如何自动生成pojo实体类.mapper类和对应的mapper.xml 文件,并实现最基本的增删改查功能.接下来要说一说Mybatis 的分页 ...

  3. Spring Boot 入门系列(二十三)整合Mybatis,实现多数据源配置!

    d之前介绍了Spring Boot 整合mybatis 使用注解方式配置的方式实现增删改查以及一些复杂自定义的sql 语句 .想必大家对spring boot 项目中,如何使用mybatis 有了一定 ...

  4. Spring Boot入门系列(十八)整合mybatis,使用注解的方式实现增删改查

    之前介绍了Spring Boot 整合mybatis 使用xml配置的方式实现增删改查,还介绍了自定义mapper 实现复杂多表关联查询.虽然目前 mybatis 使用xml 配置的方式 已经极大减轻 ...

  5. Spring Cloud Alibaba系列四:集成 seata 实现分布式事务

    文章目录 Spring Cloud Alibaba系列四:集成 seata 实现分布式事务 前言 Seata 是什么? Seata 术语 安装 seata 1.创建 seata 数据库,并添加对应的表 ...

  6. Portal-Basic Java Web 应用开发框架:应用篇(十一) —— 整合 Spring

    Portal-Basic Java Web应用开发框架(简称 Portal-Basic)是一套功能完备的高性能Full-Stack Web应用开发框架,内置稳定高效的MVC基础架构和DAO框架(已内置 ...

  7. 【Spring Data JPA自学笔记二】初识Spring Data JPA

    文章目录 Spring Data JPA是什么? Spring Data JPA的配置 配置pom.xml 配置applicationContext.xml Spring Data JPA的使用 Sp ...

  8. Spring Boot干货系列:(六)静态资源和拦截器处理 | 掘金技术征文

    原本地址:Spring Boot干货系列:(六)静态资源和拦截器处理 博客地址:tengj.top/ 前言 本章我们来介绍下SpringBoot对静态资源的支持以及很重要的一个类WebMvcConfi ...

  9. Spring Boot实战系列《六》:人事管理系统的登录设计

    Spring Boot实战系列<六>:人事管理系统的登录设计 Spring Boot实战系列<六>:人事管理系统的登录设计 1.前言 在上一篇中教大家在IEDA或者eclips ...

最新文章

  1. 网络营销专员浅析企业网站网络营销的多个优势!
  2. base64 长度补全
  3. Scheme 语言概要
  4. MyBatis查询结果resultType返回值类型详细介绍
  5. 第二十六讲:基础一开放封闭原则
  6. c++ 转bcd码_8421BCD码转换为十进制
  7. Coursera课程 Competitive Strategy内容简介
  8. 《大话设计模式》第29章-OOTV杯超级模式大赛—模式总结(五)
  9. 如何做一名了不起的开发人员?
  10. zabbix 监控项自动发现过滤_Zabbix使用javascript+jsonpath预处理动态生成监控项
  11. python中质数的表达方式_python求质数的3种方法
  12. web前端教程,详解引入CSS的4种方式
  13. 正交试验设计例题及答案_正交试验设计与数理统计作业.doc
  14. 计算机八进制 算法视频,八进制算法
  15. 二、GAMIT解算之数据准备
  16. Oracle用户、身份、数据类型、和mysql的区别
  17. 经纬财富:十堰炒白银有哪些技巧
  18. 正弦定理、余弦定理及解三角形
  19. 境外服务器停机:原因、影响以及如何预防
  20. 【电机原理与拖动基础】Unit 1 直流电机(你还不知道电机是怎么一回事吗?那就快来看一看吧!)

热门文章

  1. 【原理+实战+视频+源码】手撸SpringBoot缓存系统
  2. 【2021年度训练联盟热身训练赛第二场】Tip to be Palindrome(python)
  3. python【力扣LeetCode算法题库】1103- 分糖果 II
  4. 【Network Security!】认识进程与端口
  5. ORACLE not available如何解决
  6. mysql双机数据热备份_mysql 双机交互热备份
  7. 运筹学两阶段法编程c语言,运筹学上机实验 - 单纯形方法的两阶段法
  8. 组态王与c语言混合编程6,亚控 组态王嵌入版6.1
  9. 网络推广——符合网站现状的优化方案才是最适合进行网络推广的方案
  10. yota3墨水屏设置_国产墨水屏“手写平板”评测,10.3英寸大屏,支持无纸化办公...