《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 pdai.tech/md/spring/springboot-data-multi.html 「@pdai」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

mysql, es, mongodb 三个数据源用配置文件方式连接,JPA只是正对dao做了封装,本文主要介绍如何对service层进行封装。

类关系图

  • 对多个数据源连接获取数据进行统一封装
  • ES spring-data方式不支持多个Index和Type的查找功能,添加了DynamicESDao支持
  • 大大简化封装之后的调用, 调用方式如下

封装的一些配置

application.yml

banner:
charset: UTF-8
location: classpath:banner.txt
server:
port: 5555
contextPath: /
session:
timeout: 0
spring:
application:
name: 'spring-boot-datasource-demo'
output:
ansi:
enabled: DETECT
messages:
basename: i18n/messages
thymeleaf:
cache: false
profiles:
active: dev
# MySQL data source settings
datasource:
url: jdbc:mysql://localhost:3306/cdc_standalone?useSSL=false
username: root
password: bfXa4Pt2lUUScy8jakXf
# MySQL JPA settings
jpa:
generate-ddl: true
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true
# NoSQL data source settings
data:
# MongoDB 2.2+ settings
mongodb:
uri: mongodb://standalone:fhY1tPt1lpUSbS7jwkTf@10.11.60.4:27017/standalone
# ElasticSearch settings
elasticsearch:
cluster-name: es-logs-01
cluster-nodes: 10.11.60.5:9300

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring-boot-datasource-demo</groupId>
<artifactId>spring-boot-datasource-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-datasource-demo</name>
<description>spring-boot-datasource-demo</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<commons.lang.version>3.3.2</commons.lang.version>
<springfox.version>2.7.0</springfox.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.2.3</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

封装后使用

封装之后使用将非常简单,公共的Service行为将被封装处理

MySQL 动态数据访问

通过几行代码即可实现对MySQL的访问,同时支持动态的条件查询;

  • User
@Entity
@Table(name = "tb_user")
public class User extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 用户id
*/
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", nullable = false)
private Integer id;
private String userName;
private String nickName;
private String password;
private Integer sex;
private String telephone;
private String email;
private String address;
private Integer deleteStatus;
private Integer locked = 0;
private String description;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
@ManyToMany(cascade = { CascadeType.REFRESH }, fetch = FetchType.LAZY)
@JoinTable(name = "tb_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = {
@JoinColumn(name = "role_id") })
private java.util.Set<Role> roles;
// getter & setter
}
  • Dao
@Repositorypublic
interface IUserDao extends IBaseJpaDao<User, Integer>{}
  • Service
public interface IUserService extends IBaseJpaService<User, Integer> {
}
@Service
public class UserServiceImpl extends BaseJpaServiceImpl<User, Integer> implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public IBaseJpaDao<User, Integer> getBaseDao() {
return this.userDao;
}
@Override
public void delete(Integer id) {
User user = find(id);
Assert.state(!"admin".equals(user.getUserName()), "超级管理员用户不能删除");
super.delete(id);
}
}
  • Controller
@RestController
@RequestMapping("/admin/user")
public class UserController {
/**
* user service
*/
@Autowired
private IUserService userService;
/**
* @param searchText
* @param user
* @param model
* @return
*/
@GetMapping(value = "/list")
public List<User> list(@RequestParam(value = "searchText", required = false, defaultValue="a") String searchText) {
return userService.findAll(Specifications.<User>and().like("userName", "%" + searchText + "%").build());
}
}

Mongo 动态数据访问

通过几行代码即可实现对Mongo的访问,同时支持动态的条件查询;

  • Entity
@Document(collection = "security_alert_rules")
public class EventRule extends BaseEntity {
private static final long serialVersionUID = -2013673868028645757L;
public static final int EVENT_STATUS_ENABLE = 0, EVENT_STATUS_DISABLE = 1;
private String component;
private String name;
private String eventId;
private String ciaLevel;
private String remarks;
private String script;
private long threshold;
private long timeWindow;
private String parseEsResultKeys;
private String nameCN;
private String remarkCN;
private String ruleType;
private String redisEventKey;
private int status = EVENT_STATUS_ENABLE;
// getter & setter
}
  • Dao
@Repository
public interface IEventRuleDao extends IBaseMongoDao<EventRule, String> {
}
  • Service
public interface IEventRuleService extends IBaseMongoService<EventRule, String> {
}
@Service
public class EventRuleServiceImpl extends BaseMongoServiceImpl<EventRule, String> implements IEventRuleService {
@Autowired
IEventRuleDao eventRuleDao;
@Override
public IBaseMongoDao<EventRule, String> getBaseDao() {
return eventRuleDao;
}
}
  • Controller
@RestController
@RequestMapping("/admin/eventRule")
public class EventRuleController {
/**
*/
@Autowired
private IEventRuleService eventRuleService;
/**
* @param searchText
* @param user
* @param model
* @return
*/
@GetMapping(value = "/list")
public List<EventRule> list(@RequestParam(value = "searchText", required = false) String searchText) {
EventRule param = new EventRule();
param.setName(searchText);
// Example<EventRule> rule = Example.<EventRule>of(param,
// ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.exact()));
Example<EventRule> rule = Example.<EventRule>of(param, ExampleMatcher.matching().withIgnoreCase("name","nameCN"));
return eventRuleService.findAll(rule);
}
}

ElasticSearch 动态数据访问(单个index+type)

通过几行代码即可实现对ElasticSearch的访问,同时支持动态的条件查询;适合数据类型比较固定,且index和type独立的,比如强类型映射的实体类;

  • Entity
@Document(indexName="syslog", type="logs")
public class SysLog extends BaseEntity {
private static final long serialVersionUID = -4491916941883088972L;
@Id
private String _id;
private Set<String> phyPorts = new LinkedHashSet<>();
private Set<String> ports = new LinkedHashSet<>();
private String sensor;
private int vlan;
private Set<String> ip = new LinkedHashSet<>();
private Set<String> mac = new LinkedHashSet<>();
private String description;
private String type;
private String vendor;
private long timestamp;
private String name;
private String chassisId;
// getter & setter
}
  • Dao
@Repository
public interface ISysLogDao extends IBaseESDao<SysLog, String> {
}
  • Service
public interface ILogService extends IBaseESService<SysLog, String> {
}
@Service
public class LogServiceImpl extends BaseESServiceImpl<SysLog, String> implements ILogService {
@Autowired
ISysLogDao sysLogDao;
@Override
public IBaseESDao<SysLog, String> getBaseDao() {
return sysLogDao;
}
}
  • Controller
@RestController
@RequestMapping("/admin/log")
public class LogController {
/**
* user service
*/
@Autowired
private ILogService logService;
/**
* @param searchText
* @param user
* @param model
* @return
*/
@GetMapping(value = "/list")
public Page<SysLog> list(@RequestParam(value = "searchText", required = false) String searchText) {
return logService.search(QueryBuilders.matchQuery("_all", searchText), new PageRequest(0, 100));
}
}

ElasticSearch 动态数据访问(多个index+type)

通过几行代码即可实现对ElasticSearch的访问,同时支持动态的条件查询;适合数据类型不固定,且index和type有多个,这些index具备相同结构类型,比如syslog-EVERY-DATE(由于日志量大,将每天的日志单独存放在一个Index中);

  • Entity
@Document(indexName="syslog", type="logs")
public class SysLog extends BaseEntity {
private static final long serialVersionUID = -4491916941883088972L;
@Id
private String _id;
private Set<String> phyPorts = new LinkedHashSet<>();
private Set<String> ports = new LinkedHashSet<>();
private String sensor;
private int vlan;
private Set<String> ip = new LinkedHashSet<>();
private Set<String> mac = new LinkedHashSet<>();
private String description;
private String type;
private String vendor;
private long timestamp;
private String name;
private String chassisId;
// getter & setter
}
  • Dao
public interface IDymLogDao extends IDynamicEsDao<SysLog, String> {
}
@Repository
public class DymLogDaoImpl extends SimpleDynamicEsDaoImpl<SysLog, String> implements IDymLogDao {
@Autowired
protected ElasticsearchTemplate elasticsearchTemplate;
@Override
public ElasticsearchOperations getElasticsearchOperations() {
return elasticsearchTemplate;
}
}
  • Service
public interface IDymLogService extends IDynamicESService<SysLog, String> {
}
@Service
public class DymLogServiceImpl extends DynamicESServiceImpl<SysLog, String> implements IDymLogService {
@Autowired
IDymLogDao sysLogDao;
@Override
public IDynamicEsDao<SysLog, String> getBaseDao() {
return sysLogDao;
}
}
  • Controller
@RestController
@RequestMapping("/admin/dymLog")
public class DymLogController {
/**
* logService
*/
@Autowired
private IDymLogService logService;
/**
* @param searchText
* @param user
* @param model
* @return
*/
@GetMapping(value = "/list")
public Page<SysLog> list(
@RequestParam(value = "searchText", required = false, defaultValue = "Siemens") String searchText) {
QueryBuilder queryBuilder = QueryBuilders.matchQuery("vendor", searchText);
return logService.search(new NativeSearchQueryBuilder().withIndices("syslog-2018-12-17").withTypes("logs")
.withQuery(queryBuilder).build());
}
}

源代码托管

https://github.com/realpdai/springboot-data-multidatasource-demo

友情提示:

onemall 最近在基于 Spring Cloud Alibaba 重构,预计最迟 8 月上旬会有最新的代码。

https://github.com/YunaiV/onemall 求一波 star 嘿嘿

文章目录
  1. 1. 类关系图
  2. 2. 封装的一些配置
    1. 2.1. application.yml
    2. 2.2. pom.xml
  3. 3. 封装后使用
    1. 3.1. MySQL 动态数据访问
  4. 4. Mongo 动态数据访问
  5. 5. ElasticSearch 动态数据访问(单个index+type)
  6. 6. ElasticSearch 动态数据访问(多个index+type)
  7. 7. 源代码托管