Spring Boot集成Spring Data JPA及读写分离
Spring Data JPA是Spring社区的一个子项目,它简化了JPA的重复性代码,使得与JPA的交互更加便捷。如果我们想要在Spring Boot项目中使用JPA,那么我们可以使用Spring Data JPA轻松实现该功能。另外,当我们需要将读写操作分离到不同的数据库中,我们可以使用读写分离的方式来实现。
集成Spring Data JPA
- 引入依赖
在Spring Boot项目中使用Spring Data JPA,我们只需要在pom.xml文件中引入相应的依赖即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
- 配置数据源
在application.properties文件中配置数据源的相关信息,例如:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
- 新建实体类
在Spring Data JPA中,我们需要定义实体类来映射数据表中的字段。例如,我们需要定义一个用户实体类User,对应数据库中的user表。
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
// getter/setter
}
在实体类中,我们使用JPA注解来定义表名、字段名、主键以及相关的约束等。
- 定义JpaRepository
在Spring Data JPA中,我们使用JpaRepository来对数据进行CRUD操作。对于实体类User,我们可以创建一个JpaRepository来将其操作映射为数据库中的操作。
public interface UserRepository extends JpaRepository<User, Long> {
}
在定义JpaRepository时需要继承JpaRepository,然后指定实体类类型和主键类型。
- 实现DAO层
在使用JpaRepository时,我们需要将其注入到DAO层中进行使用。在DAO层中,我们可以通过调用JpaRepository的方法来实现对数据的CRUD操作。
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User save(User user) {
return userRepository.save(user);
}
public void deleteById(Long id) {
userRepository.deleteById(id);
}
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
public List<User> findAll() {
return userRepository.findAll();
}
}
在实现DAO层时,我们可以将JpaRepository注入到UserService中,并在其方法中调用JpaRepository的方法来实现对数据的操作。
读写分离
在实际的应用场景中,数据库的读写访问可能会产生瓶颈。为了解决这个问题,我们可以采用读写分离的方式,将读操作和写操作分别分配到不同的数据库中。
- 引入依赖
在使用读写分离的时候,我们需要引入两个额外的依赖,分别为:
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
上述两个依赖中,HikariCP是一个高性能的JDBC连接池,而spring-boot-starter-jdbc是Spring Boot的JDBC起步依赖。
- 配置数据源
在使用读写分离时,我们需要配置两个不同的数据源,分别用于读操作和写操作。
# 主库
spring.datasource.master.url=jdbc:mysql://localhost:3306/db_master
spring.datasource.master.username=root
spring.datasource.master.password=root
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
# 从库
spring.datasource.slave.url=jdbc:mysql://localhost:3306/db_slave
spring.datasource.slave.username=root
spring.datasource.slave.password=root
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
在application.properties文件中,我们分别配置了主库和从库的数据源信息。可以看到,两个数据源的配置十分相似,只有url和数据库名称不同。
- 配置数据源路由
在使用读写分离时,我们需要为数据源配置路由,使得写操作可以访问主库,而读操作可以访问从库。
@Configuration
public class DataSourceRoutingConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSourceRouter dataSourceRouter() {
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put(DatabaseType.MASTER, masterDataSource());
targetDataSources.put(DatabaseType.SLAVE, slaveDataSource());
DataSourceRouter dataSourceRouter = new DataSourceRouter();
dataSourceRouter.setDefaultTargetDataSource(masterDataSource());
dataSourceRouter.setTargetDataSources(targetDataSources);
return dataSourceRouter;
}
@Bean
public PlatformTransactionManager transactionManager(DataSourceRouter dataSourceRouter) {
return new DataSourceTransactionManager(dataSourceRouter);
}
}
在上述代码中,我们使用@Configuration注解来声明该类为配置类。其中,我们先创建了两个数据源,分别对应于主库和从库。然后,我们创建了一个DataSourceRouter,用于根据数据源类型来路由到不同的数据源。最后,我们配置了一个事务管理器,并将DataSourceRouter传入其中。
需要注意的是,在上述代码中,我们通过@ConfigurationProperties注解将application.properties文件中的数据源信息注入到了DataSource中。
在定义DatabaseType时,我们使用了一个枚举类型来存储数据源类型。
public enum DatabaseType {
MASTER, SLAVE
}
在定义DataSourceRouter时,我们使用了AbstractRoutingDataSource类,并重写了其determineCurrentLookupKey方法。该方法用于确定当前使用的数据源。
public class DataSourceRouter extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.get();
}
}
在DataSourceContextHolder中,我们使用ThreadLocal来保存当前使用的数据源类型。
public class DataSourceContextHolder {
private static final ThreadLocal<DatabaseType> CONTEXT_HOLDER = new ThreadLocal<>();
public static void set(DatabaseType databaseType) {
CONTEXT_HOLDER.set(databaseType);
}
public static DatabaseType get() {
return CONTEXT_HOLDER.get();
}
public static void clear() {
CONTEXT_HOLDER.remove();
}
}
- 配置JpaRepository
在使用读写分离的时候,我们需要根据不同的数据源类型来访问不同的数据库。这时,我们需要通过配置JpaRepository来实现该功能。
public interface UserRepositoryMaster extends JpaRepository<User, Long> {
}
public interface UserRepositorySlave extends JpaRepository<User, Long> {
}
在上述代码中,我们定义了两个JpaRepository,分别对应于主库和从库。
@Service
public class UserService {
@Autowired
private UserRepositoryMaster userRepositoryMaster;
@Autowired
private UserRepositorySlave userRepositorySlave;
public User save(User user) {
return userRepositoryMaster.save(user);
}
@DataSourceRouting(DatabaseType.MASTER)
public void deleteById(Long id) {
userRepositoryMaster.deleteById(id);
}
@DataSourceRouting(DatabaseType.SLAVE)
public User findById(Long id) {
return userRepositorySlave.findById(id).orElse(null);
}
public List<User> findAll() {
return userRepositorySlave.findAll();
}
}
在UserService中,我们将两个JpaRepository注入到其中,然后在其方法上使用@DataSourceRouting指定对应的数据源类型。
需要注意的是,在使用@DataSourceRouting时,我们需要自定义一个注解,并在方法上使用该注解来指定数据源类型。下面是自定义的@DataSourceRouting注解的代码。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceRouting {
DatabaseType value();
}
示例代码
下面是一个简单的示例代码,演示了如何在Spring Boot项目中集成Spring Data JPA并实现读写分离。
实体类User.java
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
// getter/setter
}
JpaRepository
public interface UserRepositoryMaster extends JpaRepository<User, Long> {
}
public interface UserRepositorySlave extends JpaRepository<User, Long> {
}
数据源路由配置类
@Configuration
public class DataSourceRoutingConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSourceRouter dataSourceRouter() {
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put(DatabaseType.MASTER, masterDataSource());
targetDataSources.put(DatabaseType.SLAVE, slaveDataSource());
DataSourceRouter dataSourceRouter = new DataSourceRouter();
dataSourceRouter.setDefaultTargetDataSource(masterDataSource());
dataSourceRouter.setTargetDataSources(targetDataSources);
return dataSourceRouter;
}
@Bean
public PlatformTransactionManager transactionManager(DataSourceRouter dataSourceRouter) {
return new DataSourceTransactionManager(dataSourceRouter);
}
}
UserService.java
@Service
public class UserService {
@Autowired
private UserRepositoryMaster userRepositoryMaster;
@Autowired
private UserRepositorySlave userRepositorySlave;
public User save(User user) {
return userRepositoryMaster.save(user);
}
@DataSourceRouting(DatabaseType.MASTER)
public void deleteById(Long id) {
userRepositoryMaster.deleteById(id);
}
@DataSourceRouting(DatabaseType.SLAVE)
public User findById(Long id) {
return userRepositorySlave.findById(id).orElse(null);
}
public List<User> findAll() {
return userRepositorySlave.findAll();
}
}
自定义注解@DataSourceRouting.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceRouting {
DatabaseType value();
}
枚举类DatabaseType.java
public enum DatabaseType {
MASTER, SLAVE
}
总结
本文介绍了如何在Spring Boot项目中使用Spring Data JPA,并实现了读写分离的功能。通过使用Spring Data JPA,我们可以轻松实现CRUD操作,而通过使用读写分离,我们可以将读操作和写操作分离到不同的数据库中,从而提高了数据库的访问性能。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot集成Spring Data JPA及读写分离 - Python技术站