Configuración de la base de datos de múltiples fuentes JPA (base de datos Dameng)
Este artículo presenta principalmente el marco SpringBoot para realizar la configuración de la base de datos de múltiples fuentes Jpa Esta vez, la base de datos nacional Dameng se utiliza como fuente de datos.
1. Preparación
Introduzca la dependencia de Spring Data Jpa en el archivo pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Agregue la configuración relevante de la base de datos en application.yml:
spring:
datasource:
one:
driver-class-name: dm.jdbc.driver.DmDriver
url: jdbc:dm://127.0.0.1:5236/TESTONE
username: TESTONE
password: 1234567890
type: com.alibaba.druid.pool.DruidDataSource
two:
driver-class-name: dm.jdbc.driver.DmDriver
url: jdbc:dm://127.0.0.1:5237/TESTTWO
username: TESTTWO
password: 1234567890
type: com.alibaba.druid.pool.DruidDataSource
jpa:
properties:
hibernate.dialect: org.hibernate.dialect.Oracle10gDialect
show-sql: ture
Escriba el archivo de configuración DataSourceConfig:
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.one")
@Primary
DataSource dsOne(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.two")
DataSource dsTwo(){
return DruidDataSourceBuilder.create().build();
}
}
2. Cree la configuración JPA
Cree dos configuraciones JPA diferentes basadas en la fuente de datos configurada. El código es el siguiente (ejemplo): La
primera configuración jpa:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "org.xiaoyang.dao1",//持久层路径
entityManagerFactoryRef = "entityManagerFactoryBeanOne",
transactionManagerRef = "platformTransactionManagerOne")
public class JpaConfigOne {
@Resource(name = "dsOne")
DataSource dsOne;
@Autowired
JpaProperties jpaProperties;
@Bean
@Primary
LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanOne(EntityManagerFactoryBuilder builder){
return builder.dataSource(dsOne)
.properties(jpaProperties.getProperties())
.packages("org.xiaoyang.entity")//实体类路径
.persistenceUnit("pu1")
.build();
}
@Bean
PlatformTransactionManager platformTransactionManagerOne(EntityManagerFactoryBuilder builder){
LocalContainerEntityManagerFactoryBean factoryOne = entityManagerFactoryBeanOne(builder);
return new JpaTransactionManager(factoryOne.getObject());
}
}
La segunda configuración de jpa:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "org.xiaoyang.dao2",//持久层路径
entityManagerFactoryRef = "entityManagerFactoryBeanTwo",
transactionManagerRef = "platformTransactionManagerTwo")
public class JpaConfigOne {
@Resource(name = "dsTwo")
DataSource dsTwo;
@Autowired
JpaProperties jpaProperties;
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanTwo(EntityManagerFactoryBuilder builder){
return builder.dataSource(dsTwo)
.properties(jpaProperties.getProperties())
.packages("org.xiaoyang.entity")//实体类路径
.persistenceUnit("pu1")
.build();
}
@Bean
PlatformTransactionManager platformTransactionManagerTwo(EntityManagerFactoryBuilder builder){
LocalContainerEntityManagerFactoryBean factoryOne = entityManagerFactoryBeanTwo(builder);
return new JpaTransactionManager(factoryOne.getObject());
}
}
3. Crea una clase de entidad
Cree la clase de entidad Usuario en el paquete org.xiaoyang.entity (la ruta del paquete debe ser la misma que la ruta en la clase de configuración JPA):
@Entity(name="t_user")
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String age;
//省略getter/setter方法
}
4. Crear repositorio
Cree el Repositorio en el paquete org.xiaoyang.dao1 y el paquete org.xiaoyang.dao2 (la ruta del paquete debe ser la misma que la ruta en la clase de configuración JPA):
El código UserDao1 es el siguiente:
public interface UserDao1 extends JpaRepository<User,Integer>{
}
El código UserDao2 es el siguiente:
public interface UserDao2 extends JpaRepository<User,Integer>{
}
5. Anotación + aspecto para realizar la unificación de dos fuentes de datos
Escriba una anotación para obtener la matriz del administrador de transacciones:
@Target({
ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MultiDataSourceTransactional {
/**
* 事务管理器数组
*/
String[] transactionManagers();
}
Escriba una clase de configuración que se dé cuenta de la unidad de dos fuentes de datos a través de anotaciones + aspectos:
/**
* 注解+切面实现两个数据源事物统一
*/
@Component
@Aspect
public class MultiDataSourceTransactionAspect {
/**
* 线程本地变量:为什么使用栈?※为了达到后进先出的效果※
*/
private static final ThreadLocal<Stack<Pair<JpaTransactionManager, TransactionStatus>>> THREAD_LOCAL = new ThreadLocal<>();
/**
* 用于获取事务管理器
*/
@Autowired
private ApplicationContext applicationContext;
/**
* 事务声明
*/
private DefaultTransactionDefinition def = new DefaultTransactionDefinition();
{
// 非只读模式
def.setReadOnly(false);
// 事务隔离级别:采用数据库的
def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
// 事务传播行为
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
}
/**
* 切面
* 路径必须和编写获取事务管理器数组的注解路径相同
*/
@Pointcut("@annotation(org.xiaoyang.anotations.MultiDataSourceTransactional)")
public void pointcut() {
}
/**
* 声明事务
*
* @param transactional 注解
*/
@Before("pointcut() && @annotation(transactional)")
public void before(MultiDataSourceTransactional transactional) {
// 根据设置的事务名称按顺序声明,并放到ThreadLocal里
String[] transactionManagerNames = transactional.transactionManagers();
Stack<Pair<JpaTransactionManager, TransactionStatus>> pairStack = new Stack<>();
for (String transactionManagerName : transactionManagerNames) {
JpaTransactionManager transactionManager = applicationContext.getBean(transactionManagerName, JpaTransactionManager.class);
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
pairStack.push(new Pair(transactionManager, transactionStatus));
}
THREAD_LOCAL.set(pairStack);
System.out.println("====pairStack size========");
}
/**
* 提交事务
*/
@AfterReturning("pointcut()")
public void afterReturning() {
// ※栈顶弹出(后进先出)
Stack<Pair<JpaTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get();
while (!pairStack.empty()) {
Pair<JpaTransactionManager, TransactionStatus> pair = pairStack.pop();
pair.getKey().commit(pair.getValue());
}
THREAD_LOCAL.remove();
}
/**
* 回滚事务
*/
@AfterThrowing(value = "pointcut()")
public void afterThrowing() {
// ※栈顶弹出(后进先出)
Stack<Pair<JpaTransactionManager, TransactionStatus>> pairStack = THREAD_LOCAL.get();
while (!pairStack.empty()) {
Pair<JpaTransactionManager, TransactionStatus> pair = pairStack.pop();
pair.getKey().rollback(pair.getValue());
}
THREAD_LOCAL.remove();
}
}
6. Crear servicio
Llame a la capa Dao a través del Servicio y configure la anotación para realizar la lógica empresarial: el
código de UserService es el siguiente:
public interface UserService {
//两个库同时增加数据
void addUser(User user);
//数据库一增加数据
void addUserOne(User user);
//数据库二增加数据
void addUserTwo(User user);
}
El código UserServiceImpl es el siguiente:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao1 userDao1;
@Autowired
private UserDao2 userDao2;
//同时操作两个数据库需要调用自定义注解添加事务组
@Override
@MultiDataSourceTransactional(transactionManagers = {
"platformTransactionManagerOne","platformTransactionManagerTwo"})
public void addUser(User user) {
try {
user.setName("小洋同学");
user.setAge("23");
userDao1.save(user);
userDao2.save(user);
} catch (Exception e) {
e.printStackTrace();
}
}
//单独操作数据库需要标明事务的名称
@Override
@Transactional("platformTransactionManagerOne")
public void addUserOne(User user) {
try {
user.setName("小洋同学111");
user.setAge("23");
userDao1.save(user);
} catch (Exception e) {
e.printStackTrace();
}
}
//单独操作数据库需要标明事务的名称
@Override
@Transactional("platformTransactionManagerTwo")
public void addUserTwo(User user) {
try {
user.setName("小洋同学222");
user.setAge("23");
userDao2.save(user);
} catch (Exception e) {
e.printStackTrace();
}
}
}
7. Crear controlador
Utilice el controlador para llamar a la capa de servicio para implementar la función de interfaz, el código es el siguiente:
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("/adduser")
public void addUser() {
User user= new User();
userService.addUser(user);
}
@RequestMapping("/adduserone")
public void addUserOne() {
User user= new User();
userService.addUserOne(user);
}
@RequestMapping("/addusertwo")
public void addUserTwo() {
User user= new User();
userService.addUserTwo(user);
}