[新增功能](master): 内容更新

1. JPA相关的内容提交
2. 接口规范相关的内容声明
master
土豆兄弟 3 years ago
parent 1f5c08df34
commit 9bfea90f0b

@ -104,3 +104,68 @@
1. 使用 切面+注解 的方式对同一类的请求带上相对应的验证信息
2. 注意在调用请求的时候要对内部鉴权方式和外部鉴权方式进行区分,然后知道在使用JWT方案的时候要进行JWT防止伪造鉴定
3. Nginx相关的配置
## JPA审计
### 建议使用源码
dev-protocol-jpaauditing1 (自定义审计)
dev-protocol-jpaauditing2 (内部代码实现方式)
dev-protocol-jpaauditing3 (正式环境使用)
### 原理分析
1. 从 @EnableJpaAuditing 入手分析
1. package org.springframework.data.jpa.repository.config;
2. @Import(JpaAuditingRegistrar.class)
---
Auditing 这套封装是 Spring Data JPA 实现的,而不是 Java Persistence API 规定的
注解里面还有一项重要功能就是 @Import(JpaAuditingRegistrar.class) 这个类,它帮我们处理 Auditing 的逻辑。
---
步入 org.springframework.data.jpa.repository.config.JpaAuditingRegistrar.registerBeanDefinitions 方法
1. super.registerBeanDefinitions(annotationMetadata, registry);
2. registerInfrastructureBeanWithId(...);
---
2. 打开 AuditingEntityListener 的源码
1. AuditingEntityListener 的实现还是比较简单的,利用了 Java Persistence API 里面的@PrePersist、@PreUpdate 回调函数,
在更新和创建之前通过AuditingHandler 添加了用户信息和时间信息
### 原理分析结论
1. 查看 Auditing 的实现源码,其实给我们提供了一个思路,就是怎么利用 @PrePersist、@PreUpdate 等回调函数和
@EntityListeners 定义自己的框架代码。这是值得我们学习和参考的,比如说 Auditing 的操作日志场景等。
2. 想成功配置 Auditing 功能,必须将 @EnableJpaAuditing@EntityListeners(AuditingEntityListener.class) 一起使用才有效。
3. 我们是不是可以不通过 Spring data JPA 给我们提供的 Auditing 功能,而是直接使用 @PrePersist、@PreUpdate 回调函数注解在实体上,
也可以达到同样的效果呢?答案是肯定的,因为回调函数是实现的本质。
### 整理
使用相对应的注解去注册进Spring容器
使用注册的回调函数进行注解化设置
## 正确使用 @Entity 里面的回调方法
### 概念掌握
Java Persistence API 里面规定的回调方法有哪些?
回调事件注解表
![avatar](jpa/pic/回调事件注册表.png)
- 注意事项
- 回调函数都是和 EntityManager.flush 或 EntityManager.commit 在同一个线程里面执行的,只不过调用方法有先后之分,
都是同步调用,所以当任何一个回调方法里面发生异常,都会触发事务进行回滚,而不会触发事务提交。
- Callbacks 注解可以放在实体里面,可以放在 super-class 里面,也可以定义在 entity 的 listener 里面,但需要注意的是:
放在实体(或者 super-class里面的方法签名格式为“void ()”,即没有参数,方法里面操作的是 this 对象自己;
放在实体的 EntityListener 里面的方法签名格式为“void (Object)”,也就是方法可以有参数,参数是代表用来接收回调方法的实体。
- Callbacks 注解可以放在实体里面,可以放在 super-class 里面,也可以定义在 entity 的 listener 里面,
但需要注意的是:放在实体(或者 super-class里面的方法签名格式为“void ()”,即没有参数,方法里面操作的是 this 对象自己;
放在实体的 EntityListener 里面的方法签名格式为“void (Object)”,也就是方法可以有参数,参数是代表用来接收回调方法的实体。
- JPA Callbacks 的使用方法
- 修改 BaseEntity在里面新增回调函数和注解
### 使用总结
在实体中定义一些通用逻辑, 然后在对应 Listener中进行调用时机指定 (参数的合理化和健壮性检测)
JPA Callbacks 的实现原理,事件机制
### JPA Callbacks 的最佳实践

@ -9,3 +9,99 @@
Search In Repository 直接把中央仓库的查找集成到了Idea里面。你只需要打开这款插件输入jar包的名字或者gav关键字就能查到到这个jar包所有的版本然后可以直接复制gav坐标。
VisualGC Idea堆栈的可视化工具和Idea深度集成。直接显示所有进程双击即可打开JVM的堆栈可视化界面。堆栈和垃圾收集情况一目了然
Zoolytic idea里面直接可以看zookeeper的节点信息
## 接口设计规范
- 对URL使用kebab-case短横线小写隔开形式
> 如果你想要获得订单列表。<br/>
> 错误:/systemOrders或/system_orders <br/>
> 正确:/system-orders
- 参数使用camelCase驼峰形式
> 如果想从一个特定的商店购买产品<br/>
> 错误:/system-orders/{order_id} 或者 /system-orders/{OrderId}<br/>
> 正确:/system-orders/{orderId}
- 指向集合的复数名称
> 如果你想获得系统的所有用户。<br/>
> 错误GET /user 或者 GET /User<br/>
> 正确GET /users
- URL以集合开始以标识符结束(REST接口专用)
> 如果要保持概念的单一性和一致性。<br/>
> 错误GET /shops/:shopId/category/:categoryId/price</br>
> 正确GET /shops/:shopId/或GET /category/:categoryId
- 让动词远离你的资源URL(REST接口专用)
> 不要在URL中使用动词来表达你的意图。相反使用适当的HTTP方法来描述操作。<br/>
> 错误POST /updateuser/{userId} 或 GET /getusers<br/>
> 正确 PUT /user/{userId}
- 对非资源URL使用动词
> 如果你有一个端点,它只返回一个操作。在这种情况下,你可以使用动词。例如,如果你想要向用户重新发送警报。<br/>
> POST /alarm/245743/resend <br/>
> 这些不是我们的CRUD操作。相反它们被认为是在我们的系统中执行特定工作的函数。
- JSON属性使用camelCase驼峰形式
> 如果你正在构建一个请求体或响应体为JSON的系统那么属性名应该使用驼峰大小写。<br/>
> 错误:{ user_name: "Mohammad Faisal", user_id: "1"}<br/>
> 正确:{ userName: "Mohammad Faisal", userId: "1"}
- 监控
> RESTful HTTP服务必须实现/health和/version和/metricsAPI端点。他们将提供以下信息。<br/>
> /health: 用200 OK状态码响应对/health的请求。<br/>
> /version: 用版本号响应对/version的请求。<br/>
> /metrics: 这个端点将提供各种指标,如平均响应时间。<br/>
> 也强烈推荐使用/debug和/status端点。
- 不要使用table_name作为资源名(不是特别建议)
> 不要只使用表名作为资源名。从长远来看,这种懒惰是有害的。<br/>
> 错误product_order<br/>
> 正确product-orders<br/>
- 使用简单序数作为版本
> 始终对API使用版本控制并将其向左移动使其具有最大的作用域。版本号应该是v1v2等等。<br/>
> 应该http://api.domain.com/v1/shops/3/products <br/>
> 始终在API中使用版本控制因为如果API被外部实体使用更改端点可能会破坏它们的功能。<br/>
- 在你的响应体中包括总资源数
> 如果API返回一个对象列表则响应中总是包含资源的总数。你可以为此使用total属性。<br/>
> 错误:{ users: [...]}<br/>
> 正确:{ users: [...], total: 34 }<br/>
- 接受limit和offset参数(在定义业务接口中用到,且一定要进行校验)
> 在GET操作中始终接受limit和offset参数。<br/>
> 正确GET /shops?offset=5&limit=5<br/>
> 这是因为它对于前端的分页是必要的。<br/>
- 获取字段查询参数(用于对外暴露的查询接口,严格限制数据权限)
> 返回的数据量也应该考虑在内。添加一个fields参数只公开API中必需的字段。<br/>
> 只返回商店的名称,地址和联系方式。<br/>
> GET /shops?fields=id,name,address,contact<br/>
> 在某些情况下,它还有助于减少响应大小。<br/>
- 不要在URL中通过认证令牌
> 这是一种非常糟糕的做法因为url经常被记录而身份验证令牌也会被不必要地记录。<br/>
> 错误GET /shops/123?token=some_kind_of_authenticaiton_token<br/>
> 正确:通过头部传递它们 - Authorization: Bearer xxxxxx, Extra yyyyy<br/>
> 此外,授权令牌应该是短暂有效期的。<br/>
- 验证内容类型
> 服务器不应该假定内容类型。例如如果你接受application/x-www-form-urlencoded那么攻击者可以创建一个表单并触发一个简单的POST请求。<br/>
> 因此始终验证内容类型如果你想使用默认的内容类型请使用content-type: application/json<br/>
- 对CRUD函数使用HTTP方法REST
> HTTP方法用于解释CRUD功能。<br/>
> GET检索资源的表示形式。<br/>
> POST创建新的资源和子资源。<br/>
> PUT更新现有资源。<br/>
> PATCH更新现有资源它只更新提供的字段而不更新其他字段。<br/>
> DELETE删除已存在的资源。<br/>
- 在嵌套资源的URL中使用关系
> GET /shops/2/products从shop 2获取所有产品的列表。<br/>
> GET /shops/2/products/31获取产品31的详细信息产品31属于shop 2。<br/>
> DELETE /shops/2/products/31应该删除产品31它属于商店2。<br/>
> PUT /shops/2/products/31应该更新产品31的信息只在resource-URL上使用PUT而不是集合。<br/>
> POST /shops应该创建一个新的商店并返回创建的新商店的详细信息。在集合url上使用POST。<br/>
- CORS跨源资源共享
> 一定要为所有面向公共的API支持CORS跨源资源共享头部。<br/>
> 考虑支持CORS允许的“*”来源并通过有效的OAuth令牌强制授权。<br/>
> 避免将用户凭证与原始验证相结合。<br/>
- 安全
> 在所有端点、资源和服务上实施HTTPStls加密<br/>
> 强制并要求所有回调url、推送通知端点和webhooks使用HTTPS。<br/>
- 错误
> 当客户端向服务发出无效或不正确的请求,或向服务传递无效或不正确的数据,而服务拒绝该请求时,就会出现错误,或者更具体地说,出现服务错误。<br/>
> 包括无效的身份验证凭证、不正确的参数、未知的版本id等。<br/>
> 当由于一个或多个服务错误而拒绝客户端请求时一定要返回4xx HTTP错误代码。<br/>
> 考虑处理所有属性,然后在单个响应中返回多个验证问题。<br/>
- 黄金法则
> 扁平比嵌套好。
> 简单胜于复杂。
> 字符串比数字好。
> 一致性比定制更好。

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>dev-protocol</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dev-protocol-jpaauditing</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,56 @@
package com.baiye;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* jpa
*
* @author q
* @date 2021/11/30
*/
@SpringBootApplication
public class DevProtocolJpaAuditingApplication {
public static void main(String[] args) {
// todo 这里先拿到上下文一会检查下配置
ConfigurableApplicationContext context = new SpringApplicationBuilder(DevProtocolJpaAuditingApplication.class)
.run(args);
List<String> beanNames = new ArrayList<>(Arrays.asList("db1DataSource", "db2DataSource"));
// 检查Beans加载
checkBeansWithName(context, beanNames);
}
/**
* Bean
*
* @param context
*/
private static void checkBeansWithName(ConfigurableApplicationContext context, List<String> beanNames){
Iterator<String> beanNamesIterator = context.getBeanFactory().getBeanNamesIterator();
AtomicInteger i = new AtomicInteger();
while (beanNamesIterator.hasNext()) {
String next = beanNamesIterator.next();
beanNames.forEach(
one->{
if (!next.equalsIgnoreCase(one)) {
return;
}
System.out.println("bean number is : " + (i.getAndIncrement()) + ", name is : " + next);
}
);
}
}
}

@ -0,0 +1,22 @@
package com.baiye.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
/**
* jpa
*
* @author q
* @date 2021/11/30
*/
@Configuration
@EnableJpaAuditing
public class JpaConfiguration {
@Bean
@ConditionalOnMissingBean(name = "myAuditorAware")
MyAuditorAware myAuditorAware() {
return new MyAuditorAware();
}
}

@ -0,0 +1,29 @@
package com.baiye.config;
import org.springframework.data.domain.AuditorAware;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Optional;
/**
*
*
* @author q
* @date 2021/11/30
*/
public class MyAuditorAware implements AuditorAware<Integer> {
/**
* AuditorAwareID
*
* @return {@link Optional}<{@link Integer}>
*/
@Override
public Optional<Integer> getCurrentAuditor() {
ServletRequestAttributes servletRequestAttributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert servletRequestAttributes != null;
Integer userId = (Integer) servletRequestAttributes.getRequest().getSession().getAttribute("userId");
return Optional.ofNullable(userId);
}
}

@ -0,0 +1,53 @@
package com.baiye.entity;
import lombok.*;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.EntityListeners;
import java.util.Date;
/**
*
*
* @author q
* @date 2021/11/30
*/
@Getter
@Setter
@RequiredArgsConstructor
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class AuditingBase {
/**
* id
*/
@CreatedBy
private Integer createUserId;
/**
*
*/
@CreatedDate
private Date createTime;
/**
* id
*/
@LastModifiedBy
private Integer lastModifiedUserId;
/**
*
*/
@LastModifiedDate
private Date lastModifiedTime;
}

@ -0,0 +1,35 @@
package com.baiye.entity;
import lombok.*;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
/**
*
*
* @author q
* @date 2021/11/30
*/
@Entity
@Getter
@Setter
@RequiredArgsConstructor
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString()
public class User extends AuditingBase implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String email;
private Integer age;
private Boolean deleted;
}

@ -0,0 +1,7 @@
# 加载不同的配置文件,进行配置文件分离
spring:
profiles:
include:
- jpa
# - shardingSphere
# - dynamicDatasource

@ -0,0 +1,10 @@
# 基本配置
server:
port: 8900
spring:
application:
name: dev-protocol-jpaauditing1
# 加载不同环境
profiles:
active: dev

@ -0,0 +1,28 @@
spring:
###########datasource1 采用Mysql数据库
datasource1:
url: jdbc:mysql://localhost:3306/test2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true
username: root
password: root
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
##数据源1的连接池的名字
db1:
pool-name: jpa-hikari-pool-db1
## 最长生命周期15分钟
maxLifetime: 900000
maximumPoolSize: 8
###########datasource2 采用h2内存数据库
datasource2:
url: jdbc:h2:~/test
username: sa
password: sa
datasource:
driver-class-name: org.h2.Driver
hikari:
##数据源2的连接池的名字
db2:
pool-name: jpa-hikari-pool-db2
maxLifetime: 500000
maximumPoolSize: 6

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>dev-protocol</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dev-protocol-jpaauditing2</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,56 @@
package com.baiye;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* jpa
*
* @author q
* @date 2021/11/30
*/
@SpringBootApplication
public class DevProtocolJpaAuditing2Application {
public static void main(String[] args) {
// todo 这里先拿到上下文一会检查下配置
ConfigurableApplicationContext context = new SpringApplicationBuilder(DevProtocolJpaAuditing2Application.class)
.run(args);
List<String> beanNames = new ArrayList<>(Arrays.asList("db1DataSource", "db2DataSource"));
// 检查Beans加载
checkBeansWithName(context, beanNames);
}
/**
* Bean
*
* @param context
*/
private static void checkBeansWithName(ConfigurableApplicationContext context, List<String> beanNames){
Iterator<String> beanNamesIterator = context.getBeanFactory().getBeanNamesIterator();
AtomicInteger i = new AtomicInteger();
while (beanNamesIterator.hasNext()) {
String next = beanNamesIterator.next();
beanNames.forEach(
one->{
if (!next.equalsIgnoreCase(one)) {
return;
}
System.out.println("bean number is : " + (i.getAndIncrement()) + ", name is : " + next);
}
);
}
}
}

@ -0,0 +1,78 @@
package com.baiye.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import org.hibernate.Hibernate;
import org.springframework.data.domain.Auditable;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
/**
*
*
* @author q
* @date 2021/11/30
*/
@Entity
@Getter
@Setter
@RequiredArgsConstructor
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class User implements Serializable, Auditable<Integer, Long, Instant> {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
private String email;
private Integer age;
private Integer createUserId;
private Instant createTime;
private Integer lastModifiedUserId;
private Instant lastModifiedTime;
@Override
public Optional<Integer> getCreatedBy() {
return Optional.ofNullable(this.createUserId);
}
@Override
public void setCreatedBy(Integer createdBy) {
this.createUserId = createdBy;
}
@Override
public Optional<Instant> getCreatedDate() {
return Optional.ofNullable(this.createTime);
}
@Override
public void setCreatedDate(Instant creationDate) {
this.createTime = creationDate;
}
@Override
public Optional<Integer> getLastModifiedBy() {
return Optional.ofNullable(this.lastModifiedUserId);
}
@Override
public void setLastModifiedBy(Integer lastModifiedBy) {
this.lastModifiedUserId = lastModifiedBy;
}
@Override
public void setLastModifiedDate(Instant lastModifiedDate) {
this.lastModifiedTime = lastModifiedDate;
}
@Override
public Optional<Instant> getLastModifiedDate() {
return Optional.ofNullable(this.lastModifiedTime);
}
@Override
public boolean isNew() {
return id==null;
}
}

@ -0,0 +1,7 @@
# 加载不同的配置文件,进行配置文件分离
spring:
profiles:
include:
- jpa
# - shardingSphere
# - dynamicDatasource

@ -0,0 +1,10 @@
# 基本配置
server:
port: 8900
spring:
application:
name: dev-protocol-jpaauditing2
# 加载不同环境
profiles:
active: dev

@ -0,0 +1,28 @@
spring:
###########datasource1 采用Mysql数据库
datasource1:
url: jdbc:mysql://localhost:3306/test2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true
username: root
password: root
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
##数据源1的连接池的名字
db1:
pool-name: jpa-hikari-pool-db1
## 最长生命周期15分钟
maxLifetime: 900000
maximumPoolSize: 8
###########datasource2 采用h2内存数据库
datasource2:
url: jdbc:h2:~/test
username: sa
password: sa
datasource:
driver-class-name: org.h2.Driver
hikari:
##数据源2的连接池的名字
db2:
pool-name: jpa-hikari-pool-db2
maxLifetime: 500000
maximumPoolSize: 6

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>dev-protocol</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dev-protocol-jpaauditing3</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,41 @@
package com.baiye.entity;
import lombok.*;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.Instant;
/**
*
*
* ps: Auditing RestAPI API UserID
* @author q
* @date 2021/11/30
* @see EntityListeners(AuditingEntityListener) ,
*/
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
@CreatedBy
private Integer createUserId;
@CreatedDate
private Instant createTime;
@LastModifiedBy
private Integer lastModifiedUserId;
@LastModifiedDate
private Instant lastModifiedTime;
@Version
private Integer version;
}

@ -0,0 +1,27 @@
package com.baiye.entity;
import lombok.*;
import javax.persistence.*;
/**
* 使
*
*
* @author q
* @date 2021/11/30
*/
@Entity
@Getter
@Setter
@RequiredArgsConstructor
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User extends BaseEntity{
private String name;
private String email;
private Integer age;
private Boolean deleted;
}

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>dev-protocol</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dev-protocol-jpaentity-callback</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

@ -0,0 +1,4 @@
package com.baiye;
public class DevProtocolJpaEntityCallbackApplication {
}

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>dev-protocol</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dev-protocol-jpashard1</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,38 @@
package com.baiye;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.Iterator;
/**
* appication
*
* @author q
* @date 2021/11/29
*/
@SpringBootApplication
public class DevProtocolJpaShard1Application {
public static void main(String[] args) {
// todo 这里先拿到上下文一会检查下配置
ConfigurableApplicationContext context = new SpringApplicationBuilder(DevProtocolJpaShard1Application.class)
.run(args);
// 检查Beans加载
checkAllBeansWithName(context);
}
private static void checkAllBeansWithName(ConfigurableApplicationContext context) {
Iterator<String> beanNamesIterator = context.getBeanFactory().getBeanNamesIterator();
int i = 0;
while (beanNamesIterator.hasNext()) {
System.out.println("bean number is : " + (i++) + "name is : " + beanNamesIterator.next());
}
}
}

@ -0,0 +1,95 @@
package com.baiye.configuration;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.StringUtils;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
/**
* source1
*
*
*
* @author q
* @date 2021/11/29
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"com.baiye.dao.db1"},//数据源1的repository的包路径
entityManagerFactoryRef = "db1EntityManagerFactory",//改变数据源1的EntityManagerFactory的默认值改为db1EntityManagerFactory
transactionManagerRef = "db1TransactionManager"//改变数据源1的transactionManager的默认值改为db1TransactionManager
)
public class DataSource1Config {
/**
* 1dataSource
*
* @see ConfigurationProperties 1dbspring.datasource1
*/
@Primary
@Bean(name = "db1DataSourceProperties")
@ConfigurationProperties("spring.datasource1")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
/**
* HikariDataSource1
*
* @see ConfigurationProperties 1hikarikey
* @param db1DataSourceProperties
* @return
*/
@Primary
@Bean(name = "db1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.hikari.db1")
public HikariDataSource dataSource(@Qualifier("db1DataSourceProperties") DataSourceProperties db1DataSourceProperties) {
HikariDataSource dataSource = db1DataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (StringUtils.hasText(db1DataSourceProperties.getName())) {
dataSource.setPoolName(db1DataSourceProperties.getName());
}
return dataSource;
}
/**
* 1entityManagerFactorydb1EntityManagerFactory
* @param builder
* @param db1DataSource entityManagerdb1DataSource
* @return
*/
@Primary
@Bean(name = "db1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("db1DataSource") DataSource db1DataSource) {
return builder.dataSource(db1DataSource)
//数据1的实体所在的路径
.packages("com.baiye.dao.db1")
// persistenceUnit的名字采用db1
.persistenceUnit("db1")
.build();
}
/**
* 1db1TransactionManagerdb1EntityManagerFactory
* @param db1EntityManagerFactory
* @return
*/
@Primary
@Bean(name = "db1TransactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("db1EntityManagerFactory") EntityManagerFactory db1EntityManagerFactory) {
return new JpaTransactionManager(db1EntityManagerFactory);
}
}

@ -0,0 +1,93 @@
package com.baiye.configuration;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.StringUtils;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
/**
* source2
*
* @author q
* @date 2021/11/29
*/
@Configuration
@EnableTransactionManagement//开启事务
//利用EnableJpaRepositories配置哪些包下面的Repositories采用哪个 EntityManagerFactory 和哪个 transactionManager
@EnableJpaRepositories(
basePackages = {"com.baiye.dao.db2"},//数据源2的repository的包路径
entityManagerFactoryRef = "db2EntityManagerFactory",//改变数据源2的EntityManagerFactory的默认值改为db2EntityManagerFactory
transactionManagerRef = "db2TransactionManager"//改变数据源2的transactionManager的默认值改为db2TransactionManager
)
public class DataSource2Config {
/**
* 2dataSource
*
* 2dbspring.datasource2
* @return
*/
@Bean(name = "db2DataSourceProperties")
@ConfigurationProperties("spring.datasource2")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
/**
* HikariDataSource2
*
* 2hikarikey
*
* @param db2DataSourceProperties
* @return
*/
@Bean(name = "db2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.hikari.db2")
public HikariDataSource dataSource(@Qualifier("db2DataSourceProperties") DataSourceProperties db2DataSourceProperties) {
HikariDataSource dataSource = db2DataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (StringUtils.hasText(db2DataSourceProperties.getName())) {
dataSource.setPoolName(db2DataSourceProperties.getName());
}
return dataSource;
}
/**
* 2entityManagerFactorydb2EntityManagerFactory
*
* @param builder
* @param db2DataSource entityManagerdb2DataSource
* @return
*/
@Bean(name = "db2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("db2DataSource") DataSource db2DataSource) {
return builder.dataSource(db2DataSource)
//数据2的实体所在的路径
.packages("com.baiye.dao.db2")
// persistenceUnit的名字采用db2
.persistenceUnit("db2")
.build();
}
/**
* 2db2TransactionManagerdb2EntityManagerFactory
*
* @param db2EntityManagerFactory
* @return
*/
@Bean(name = "db2TransactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("db2EntityManagerFactory") EntityManagerFactory db2EntityManagerFactory) {
return new JpaTransactionManager(db2EntityManagerFactory);
}
}

@ -0,0 +1,35 @@
package com.baiye.controller;
import com.baiye.dao.db1.Order;
import com.baiye.dao.db1.OrderRepository;
import com.baiye.dao.db2.Sku;
import com.baiye.dao.db2.SkuRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
private OrderRepository orderRepository;
@Autowired
private SkuRepository skuRepository;
/**
*
*
* @param order
* @return {@link Order}
*/
@PostMapping("/user")
public Order saveOrder(@RequestBody Order order) {
return orderRepository.save(order);
}
@PostMapping("/user/info")
public Sku saveUserInfo(@RequestBody Sku sku) {
return skuRepository.save(sku);
}
}

@ -0,0 +1,42 @@
package com.baiye.dao.db1;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
/**
* -1
*
* @author q
* @date 2021/11/29
*/
@Entity
@Table(name = "order")
public class Order {
/**
* id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
*
*/
@Column(name = "order_name")
private String orderName;
/**
*
*/
@Column(name = "order_price")
private BigDecimal orderPrice;
/**
*
*/
@Column(name = "order_time")
private Date orderTime;
}

@ -0,0 +1,14 @@
package com.baiye.dao.db1;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
*
*
* @author q
* @date 2021/11/29
*/
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
}

@ -0,0 +1,42 @@
package com.baiye.dao.db2;
import javax.persistence.*;
/**
* sku -2
*
* @author q
* @date 2021/11/29
*/
@Entity
@Table(name = "sku")
public class Sku {
/**
* id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
*
*/
private String name;
/**
*
*/
private String level;
/**
*
*/
private String color;
/**
*
*/
private String size;
}

@ -0,0 +1,14 @@
package com.baiye.dao.db2;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* sku
*
* @author q
* @date 2021/11/29
*/
@Repository
public interface SkuRepository extends JpaRepository<Sku, Long> {
}

@ -0,0 +1,7 @@
# 加载不同的配置文件,进行配置文件分离
spring:
profiles:
include:
- jpa
# - shardingSphere
# - dynamicDatasource

@ -0,0 +1,10 @@
# 基本配置
server:
port: 8900
spring:
application:
name: dev-protocol-jpashard1
# 加载不同环境
profiles:
active: dev

@ -0,0 +1,28 @@
spring:
###########datasource1 采用Mysql数据库
datasource1:
url: jdbc:mysql://localhost:3306/test2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true
username: root
password: root
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
##数据源1的连接池的名字
db1:
pool-name: jpa-hikari-pool-db1
## 最长生命周期15分钟
maxLifetime: 900000
maximumPoolSize: 8
###########datasource2 采用h2内存数据库
datasource2:
url: jdbc:h2:~/test
username: sa
password: sa
datasource:
driver-class-name: org.h2.Driver
hikari:
##数据源2的连接池的名字
db2:
pool-name: jpa-hikari-pool-db2
maxLifetime: 500000
maximumPoolSize: 6

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>dev-protocol</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dev-protocol-jpashard2</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,56 @@
package com.baiye;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* jpa shard2
*
* @author q
* @date 2021/11/30
*/
@SpringBootApplication
public class DevProtocolJpaShard2Application {
public static void main(String[] args) {
// todo 这里先拿到上下文一会检查下配置
ConfigurableApplicationContext context = new SpringApplicationBuilder(DevProtocolJpaShard2Application.class)
.run(args);
List<String> beanNames = new ArrayList<>(Arrays.asList("db1DataSource", "db2DataSource"));
// 检查Beans加载
checkBeansWithName(context, beanNames);
}
/**
* Bean
*
* @param context
*/
private static void checkBeansWithName(ConfigurableApplicationContext context, List<String> beanNames){
Iterator<String> beanNamesIterator = context.getBeanFactory().getBeanNamesIterator();
AtomicInteger i = new AtomicInteger();
while (beanNamesIterator.hasNext()) {
String next = beanNamesIterator.next();
beanNames.forEach(
one->{
if (!next.equalsIgnoreCase(one)) {
return;
}
System.out.println("bean number is : " + (i.getAndIncrement()) + ", name is : " + next);
}
);
}
}
}

@ -0,0 +1,95 @@
package com.baiye.configuration;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.StringUtils;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
/**
* source1
*
*
*
* @author q
* @date 2021/11/29
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"com.baiye.dao.db1"},//数据源1的repository的包路径
entityManagerFactoryRef = "db1EntityManagerFactory",//改变数据源1的EntityManagerFactory的默认值改为db1EntityManagerFactory
transactionManagerRef = "db1TransactionManager"//改变数据源1的transactionManager的默认值改为db1TransactionManager
)
public class DataSource1Config {
/**
* 1dataSource
*
* @see ConfigurationProperties 1dbspring.datasource1
*/
@Primary
@Bean(name = "db1DataSourceProperties")
@ConfigurationProperties("spring.datasource1")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
/**
* HikariDataSource1
*
* @see ConfigurationProperties 1hikarikey
* @param db1DataSourceProperties
* @return
*/
@Primary
@Bean(name = "db1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.hikari.db1")
public HikariDataSource dataSource(@Qualifier("db1DataSourceProperties") DataSourceProperties db1DataSourceProperties) {
HikariDataSource dataSource = db1DataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (StringUtils.hasText(db1DataSourceProperties.getName())) {
dataSource.setPoolName(db1DataSourceProperties.getName());
}
return dataSource;
}
/**
* 1entityManagerFactorydb1EntityManagerFactory
* @param builder
* @param db1DataSource entityManagerdb1DataSource
* @return
*/
@Primary
@Bean(name = "db1EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("db1DataSource") DataSource db1DataSource) {
return builder.dataSource(db1DataSource)
//数据1的实体所在的路径
.packages("com.baiye.dao.db1")
// persistenceUnit的名字采用db1
.persistenceUnit("db1")
.build();
}
/**
* 1db1TransactionManagerdb1EntityManagerFactory
* @param db1EntityManagerFactory
* @return
*/
@Primary
@Bean(name = "db1TransactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("db1EntityManagerFactory") EntityManagerFactory db1EntityManagerFactory) {
return new JpaTransactionManager(db1EntityManagerFactory);
}
}

@ -0,0 +1,93 @@
package com.baiye.configuration;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.StringUtils;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
/**
* source2
*
* @author q
* @date 2021/11/29
*/
@Configuration
@EnableTransactionManagement//开启事务
//利用EnableJpaRepositories配置哪些包下面的Repositories采用哪个 EntityManagerFactory 和哪个 transactionManager
@EnableJpaRepositories(
basePackages = {"com.baiye.dao.db2"},//数据源2的repository的包路径
entityManagerFactoryRef = "db2EntityManagerFactory",//改变数据源2的EntityManagerFactory的默认值改为db2EntityManagerFactory
transactionManagerRef = "db2TransactionManager"//改变数据源2的transactionManager的默认值改为db2TransactionManager
)
public class DataSource2Config {
/**
* 2dataSource
*
* 2dbspring.datasource2
* @return
*/
@Bean(name = "db2DataSourceProperties")
@ConfigurationProperties("spring.datasource2")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
/**
* HikariDataSource2
*
* 2hikarikey
*
* @param db2DataSourceProperties
* @return
*/
@Bean(name = "db2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.hikari.db2")
public HikariDataSource dataSource(@Qualifier("db2DataSourceProperties") DataSourceProperties db2DataSourceProperties) {
HikariDataSource dataSource = db2DataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
if (StringUtils.hasText(db2DataSourceProperties.getName())) {
dataSource.setPoolName(db2DataSourceProperties.getName());
}
return dataSource;
}
/**
* 2entityManagerFactorydb2EntityManagerFactory
*
* @param builder
* @param db2DataSource entityManagerdb2DataSource
* @return
*/
@Bean(name = "db2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, @Qualifier("db2DataSource") DataSource db2DataSource) {
return builder.dataSource(db2DataSource)
//数据2的实体所在的路径
.packages("com.baiye.dao.db2")
// persistenceUnit的名字采用db2
.persistenceUnit("db2")
.build();
}
/**
* 2db2TransactionManagerdb2EntityManagerFactory
*
* @param db2EntityManagerFactory
* @return
*/
@Bean(name = "db2TransactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("db2EntityManagerFactory") EntityManagerFactory db2EntityManagerFactory) {
return new JpaTransactionManager(db2EntityManagerFactory);
}
}

@ -0,0 +1,23 @@
package com.baiye.configuration;
/**
* ThreadLocal线使
*
* @author q
* @date 2021/11/30
*/
public class DataSourceRoutingHolder {
private static ThreadLocal<RoutingDataSourceEnum> threadLocal = new ThreadLocal<>();
public static void setBranchContext(RoutingDataSourceEnum dataSourceEnum) {
threadLocal.set(dataSourceEnum);
}
public static RoutingDataSourceEnum getBranchContext() {
return threadLocal.get();
}
public static void clearBranchContext() {
threadLocal.remove();
}
}

@ -0,0 +1,24 @@
package com.baiye.configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.Optional;
/**
* AbstractRoutingDataSource线
*
* @author q
* @date 2021/11/30
*/
public class RoutingDataSource extends AbstractRoutingDataSource {
/**
* keyDB1
*
* @return {@link Object}
*/
@Override
protected Object determineCurrentLookupKey() {
return Optional.ofNullable(DataSourceRoutingHolder.getBranchContext()).orElse(RoutingDataSourceEnum.DB1);
}
}

@ -0,0 +1,89 @@
package com.baiye.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
*
*
* @author q
* @date 2021/11/30
*/
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
//数据源的repository的包路径这里我们覆盖db1和db2的包路径
basePackages = {"com.baiye.dao"},
entityManagerFactoryRef = "routingEntityManagerFactory",
transactionManagerRef = "routingTransactionManager"
)
public class RoutingDataSourceConfig {
@Autowired
@Qualifier(value = "db1DataSource")
private DataSource db1DataSource;
@Autowired
@Qualifier(value = "db2DataSource")
private DataSource db2DataSource;
/**
* RoutingDataSourcedb1DataSourcedb2DataSource
*
* @return
*/
@Bean(name = "routingDataSource")
public DataSource dataSource() {
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(RoutingDataSourceEnum.DB1, db1DataSource);
dataSourceMap.put(RoutingDataSourceEnum.DB2, db2DataSource);
RoutingDataSource routingDataSource = new RoutingDataSource();
//设置RoutingDataSource的默认数据源
routingDataSource.setDefaultTargetDataSource(db1DataSource);
//设置RoutingDataSource的数据源列表
routingDataSource.setTargetDataSources(dataSourceMap);
return routingDataSource;
}
/**
* db1db2routingDataSource
*
* @param builder
* @param routingDataSource entityManagerroutingDataSource
* @return
*/
@Bean(name = "routingEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
@Qualifier("routingDataSource") DataSource routingDataSource) {
//数据routing的实体所在的路径这里我们覆盖db1和db2的路径
return builder.dataSource(routingDataSource).packages("com.baiye.dao")
// persistenceUnit的名字采用db-routing
.persistenceUnit("db-routing")
.build();
}
/**
* routingTransactionManagerrouttingEntityManagerFactory
*
* @param routingEntityManagerFactory
* @return
*/
@Bean(name = "routingTransactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("routingEntityManagerFactory") EntityManagerFactory routingEntityManagerFactory) {
return new JpaTransactionManager(routingEntityManagerFactory);
}
}

@ -0,0 +1,22 @@
package com.baiye.configuration;
/**
*
*
* @author q
* @date 2021/11/30
*/
public enum RoutingDataSourceEnum {
// fixme 实际工作中枚举的语义可以更加明确一点;
DB1,
DB2;
public static RoutingDataSourceEnum findbyCode(String dbRouting) {
for (RoutingDataSourceEnum e : values()) {
if (e.name().equals(dbRouting)) {
return e;
}
}
//没找到的情况下默认返回数据源1
return DB1;
}
}

@ -0,0 +1,42 @@
package com.baiye.dao.db1;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
/**
* -1
*
* @author q
* @date 2021/11/29
*/
@Entity
@Table(name = "order")
public class Order {
/**
* id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
*
*/
@Column(name = "order_name")
private String orderName;
/**
*
*/
@Column(name = "order_price")
private BigDecimal orderPrice;
/**
*
*/
@Column(name = "order_time")
private Date orderTime;
}

@ -0,0 +1,14 @@
package com.baiye.dao.db1;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
*
*
* @author q
* @date 2021/11/29
*/
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
}

@ -0,0 +1,42 @@
package com.baiye.dao.db2;
import javax.persistence.*;
/**
* sku -2
*
* @author q
* @date 2021/11/29
*/
@Entity
@Table(name = "sku")
public class Sku {
/**
* id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
*
*/
private String name;
/**
*
*/
private String level;
/**
*
*/
private String color;
/**
*
*/
private String size;
}

@ -0,0 +1,14 @@
package com.baiye.dao.db2;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* sku
*
* @author q
* @date 2021/11/29
*/
@Repository
public interface SkuRepository extends JpaRepository<Sku, Long> {
}

@ -0,0 +1,39 @@
package com.baiye.web;
import com.baiye.configuration.DataSourceRoutingHolder;
import com.baiye.configuration.RoutingDataSourceEnum;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
*
* @author q
* @date 2021/11/30
*/
@Component
public class DataSourceInterceptor extends HandlerInterceptorAdapter {
/**
* 线
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String dbRouting = request.getHeader("db-routing");
DataSourceRoutingHolder.setBranchContext(RoutingDataSourceEnum.findbyCode(dbRouting));
return super.preHandle(request, response, handler);
}
/**
* 线
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
DataSourceRoutingHolder.clearBranchContext();
}
}

@ -0,0 +1,7 @@
# 加载不同的配置文件,进行配置文件分离
spring:
profiles:
include:
- jpa
# - shardingSphere
# - dynamicDatasource

@ -0,0 +1,12 @@
# 基本配置
server:
port: 8900
spring:
application:
name: dev-protocol-jpashard1
# 加载不同环境
profiles:
active: dev
main:
allow-bean-definition-overriding: true

@ -0,0 +1,28 @@
spring:
###########datasource1 采用Mysql数据库
datasource1:
url: jdbc:mysql://localhost:3306/test2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true
username: root
password: root
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
##数据源1的连接池的名字
db1:
pool-name: jpa-hikari-pool-db1
## 最长生命周期15分钟
maxLifetime: 900000
maximumPoolSize: 8
###########datasource2 采用h2内存数据库
datasource2:
url: jdbc:h2:~/test
username: sa
password: sa
datasource:
driver-class-name: org.h2.Driver
hikari:
##数据源2的连接池的名字
db2:
pool-name: jpa-hikari-pool-db2
maxLifetime: 500000
maximumPoolSize: 6

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

@ -15,6 +15,12 @@
<module>dev-protocol-gateway</module>
<module>dev-protocol-devops</module>
<module>dev-protocol-shardingtask</module>
<module>jpa/dev-protocol-jpa-shard2</module>
<module>jpa/dev-protocol-jpa-shard1</module>
<module>jpa/dev-protocol-jpa-auditing1</module>
<module>jpa/dev-protocol-jpa-auditing2</module>
<module>jpa/dev-protocol-jpa-auditing3</module>
<module>jpa/dev-protocol-jpa-entity-callback</module>
</modules>
<properties>

Loading…
Cancel
Save