以下是使用 PostgreSQL 数据库的完整配置,结合 MyBatis 和 JTA (Atomikos) 实现分布式事务。
1. 项目结构(与之前相同,仅调整数据库相关配置)
src/
├── main/
│ ├── java/
│ │ └── com/example/
│ │ ├── config/ # 配置类
│ │ │ ├── Db1Config.java
│ │ │ ├── Db2Config.java
│ │ ├── mapper/ # MyBatis Mapper接口
│ │ │ ├── db1/UserMapper.java
│ │ │ └── db2/OrderMapper.java
│ │ ├── model/ # 实体类
│ │ │ ├── User.java
│ │ │ └── Order.java
│ │ ├── service/ # 服务层
│ │ │ └── OrderService.java
│ │ └── Application.java # 启动类
│ └── resources/
│ ├── mapper/ # MyBatis XML
│ │ ├── db1/UserMapper.xml
│ │ └── db2/OrderMapper.xml
│ └── application.yml # 配置文件
2. 依赖配置(pom.xml)
替换 MySQL 驱动为 PostgreSQL 驱动:
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JTA (Atomikos) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- PostgreSQL 驱动 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
</dependencies>
3. 数据源配置(application.yml)
调整数据库驱动和连接参数:
spring:
jta:
enabled: true
atomikos:
properties:
log-base-dir: ./transaction-logs
default-timeout: 60s
datasource:
db1:
xa-data-source-class-name: org.postgresql.xa.PGXADataSource # PostgreSQL XA 驱动
xa-properties:
url: jdbc:postgresql://localhost:5432/db1
user: postgres
password: 123456
unique-resource-name: db1
db2:
xa-data-source-class-name: org.postgresql.xa.PGXADataSource # PostgreSQL XA 驱动
xa-properties:
url: jdbc:postgresql://localhost:5432/db2
user: postgres
password: 123456
unique-resource-name: db2
4. MyBatis 多数据源配置类
(1) 主数据源配置(Db1Config.java)
@Configuration
@MapperScan(
basePackages = "com.example.mapper.db1",
sqlSessionFactoryRef = "sqlSessionFactoryDb1"
)
public class Db1Config {
@Bean(name = "dataSourceDb1")
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource dataSourceDb1() {
return new AtomikosDataSourceBean();
}
@Bean(name = "sqlSessionFactoryDb1")
public SqlSessionFactory sqlSessionFactoryDb1(
@Qualifier("dataSourceDb1") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/db1/*.xml")
);
return factoryBean.getObject();
}
}
(2) 次数据源配置(Db2Config.java)
@Configuration
@MapperScan(
basePackages = "com.example.mapper.db2",
sqlSessionFactoryRef = "sqlSessionFactoryDb2"
)
public class Db2Config {
@Bean(name = "dataSourceDb2")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource dataSourceDb2() {
return new AtomikosDataSourceBean();
}
@Bean(name = "sqlSessionFactoryDb2")
public SqlSessionFactory sqlSessionFactoryDb2(
@Qualifier("dataSourceDb2") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/db2/*.xml")
);
return factoryBean.getObject();
}
}
5. MyBatis Mapper 接口与 XML
(1) UserMapper.java(db1 数据库)
package com.example.mapper.db1;
@Mapper
public interface UserMapper {
@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
void insertUser(User user);
}
(2) UserMapper.xml
<!-- src/main/resources/mapper/db1/UserMapper.xml -->
<mapper namespace="com.example.mapper.db1.UserMapper">
<insert id="insertUser" parameterType="com.example.model.User">
INSERT INTO users(name, email)
VALUES(#{name}, #{email})
</insert>
</mapper>
(3) OrderMapper.java(db2 数据库)
package com.example.mapper.db2;
@Mapper
public interface OrderMapper {
@Insert("INSERT INTO orders(user_id, amount) VALUES(#{userId}, #{amount})")
void insertOrder(Order order);
}
(4) OrderMapper.xml
<!-- src/main/resources/mapper/db2/OrderMapper.xml -->
<mapper namespace="com.example.mapper.db2.OrderMapper">
<insert id="insertOrder" parameterType="com.example.model.Order">
INSERT INTO orders(user_id, amount)
VALUES(#{userId}, #{amount})
</insert>
</mapper>
6. 服务层与事务管理(与 MySQL 版本一致)
@Service
public class OrderService {
@Autowired
private UserMapper userMapper; // 操作 db1 的 Mapper
@Autowired
private OrderMapper orderMapper; // 操作 db2 的 Mapper
@Transactional // 使用 JTA 全局事务
public void createOrder(User user, Order order) {
userMapper.insertUser(user); // 插入 db1
orderMapper.insertOrder(order); // 插入 db2
// 若此处抛出异常,两个插入操作将同时回滚
}
// 测试事务回滚
public void testRollback() {
User user = new User("Alice", "alice@example.com");
Order order = new Order(1L, 100.0);
try {
createOrder(user, order);
} catch (Exception e) {
System.out.println("事务已回滚");
}
}
}
7. 启动类(Application.java)
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
8. 测试与验证
-
创建 PostgreSQL 表:
-- db1.users CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(50), email VARCHAR(50) ); -- db2.orders CREATE TABLE orders ( id SERIAL PRIMARY KEY, user_id INT, amount DECIMAL(10,2) );
-
运行测试方法:
@SpringBootTest public class OrderServiceTest { @Autowired private OrderService orderService; @Test public void testDistributedTransaction() { orderService.testRollback(); } }
-
验证结果:
- 若
createOrder()
方法无异常,两个数据库均插入数据。 - 若方法内抛出异常,两个数据库均无数据插入。
- 若
关键调整点总结
-
依赖调整:替换 MySQL 驱动为 PostgreSQL 驱动。
-
数据源配置:
xa-data-source-class-name
:org.postgresql.xa.PGXADataSource
。- 数据库 URL 格式:
jdbc:postgresql://host:port/database
。
-
事务管理器:无需修改,Spring Boot 自动配置
JtaTransactionManager
。 -
SQL 语法:注意 PostgreSQL 与 MySQL 的差异(如自增主键
SERIAL
)。
通过以上配置,MyBatis 和 JTA 可在 PostgreSQL 中实现分布式事务管理。