spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询

JPA的继承注解一般有四种

@MappedSuperclass 这个注解应用的场景是父类不对应任何单独的表,多个子类共用相同的属性。
注意:
@MappedSuperclass注解使用在父类上面,是用来标识父类的作用
@MappedSuperclass标识的类表示其不能映射到数据库表,因为其不是一个完整的实体类,但是它所拥有的属性能够映射在 其子类对用的数据库表中
@MappedSuperclass标识得类不能再有@Entity或@Table注解 但是可以使用@Id 和@Column注解

@Inheritence 此注解应用于根实体类以定义继承策略。 如果没有使用此注释定义策略类型,那么它遵循单表战略。
单表策略:
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
查询父类关联的表 在查询父类的时候 所有子类表中字段全部查询出来
连接策略:
@Inheritance(strategy=InheritanceType.JOINED)
在连接策略中,为每个实体类生成一个单独的表。 每个表的属性都与主键连接。 它消除了字段字重复的可能性。但是父类中除了主键的的其他字段 并不会在子表中查询出来
按类表策略:
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
在按类表策略中,为每个子实体类生成一个单独的表。 与连接策略不同,在按类表策略中不会为父实体类生成单独的表
@DiscriminatorColumn 鉴别器属性将一个实体与另一个实体区分开来。 因此,该注释用于提供鉴别器列的名称。 仅需要在根实体类上指定此注释。
@DiscriminatorValue 此注释用于指定表示特定实体的值的类型。 需要在子实体类中指定此注释。

@MappedSuperclass 测试

准备两张表:t_user,t_address。
t_user

CREATE TABLE `t_user` (
  `rid` bigint NOT NULL,
  `user_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `email` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `phone` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

t_address

CREATE TABLE `t_address` (
  `rid` bigint NOT NULL,
  `province` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `city` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `county` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

BaseEntity 实体类,使用@MappedSuperclass注解,标识属性能够映射在其子类对用的数据库表中

@MappedSuperclass
public class BaseEntity {
    @Id
    @Column(name = "rid",nullable = false)
    private Long rid;

    public Long getRid() {
        return rid;
    }

    public void setRid(Long rid) {
        this.rid = rid;
    }
}

t_user实体类:

@Entity
@Table(name = "t_address")
public class Address extends BaseEntity{
    @Column(name = "province")
    private String province;
    @Column(name = "city")
    private String city;
    @Column(name = "county")
    private String county;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCounty() {
        return county;
    }

    public void setCounty(String county) {
        this.county = county;
    }
}

t_address:

@Entity
@Table(name = "t_address")
public class Address extends BaseEntity{
    @Column(name = "province")
    private String province;
    @Column(name = "city")
    private String city;
    @Column(name = "county")
    private String county;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCounty() {
        return county;
    }

    public void setCounty(String county) {
        this.county = county;
    }
}

userRepository接口继承JpaRepository,JpaSpecificationExecutor实现crud操作。

public interface UserRepository extends JpaRepository<User,Long>,JpaSpecificationExecutor<User> {
}

addressRepository接口继承JpaRepository,JpaSpecificationExecutor实现crud操作。

public interface AddressRepository extends JpaRepository<Address,Long>, JpaSpecificationExecutor<Address> {
}

配置数据库连接在application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1:3306/testdemo?serverTimezone=UTC

测试:

    @Autowired
    private AddressRepository addressRepository;
    @Test
    void contextLoads() {
        Address address = new Address();
        address.setRid(1L);
        address.setProvince("四川省");
        address.setCity("成都市");
        address.setCounty("金牛区");
        addressRepository.save(address);
    }

spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第1张图片

    @Autowired
    private UserRepository userRepository;
    @Test
    void userTest(){
        User user = new User();
        user.setRid(1L);
        user.setUserName("祝八一");
        user.setEmail("[email protected]");
        user.setPhone("1878265xxxx");
        userRepository.save(user);
    }

spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第2张图片
可见在baseEntity里面的rid也插入进了数据库

jpa配置多数据源

1.首先把application.yml里面的数据源的配置先注释了。

spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第3张图片
换成如下的配置

spring:
  datasource:
    db1:
      url: jdbc:mysql://127.0.0.1:3306/testdemo?serverTimezone=UTC
      #数据库用户名
      username: root
      #数据库密码
      password: 123456
      #mysql数据库驱动程序(重要)
      driverClassName: com.mysql.cj.jdbc.Driver
    db2:
      url: jdbc:mysql://127.0.0.1:3306/shop?serverTimezone=UTC
      #数据库用户名
      username: root
      #数据库密码
      password: 123456
      #mysql数据库驱动程序(重要)
      driverClassName: com.mysql.cj.jdbc.Driver

2.编写数据源配置类

@ConfigurationProperties(prefix = "spring.datasource.db1")
@Component
@Data
public class Db1Properties {
    private String url;
    private String username;
    private String password;
    private String driverClassName;
}
@ConfigurationProperties(prefix = "spring.datasource.db2")
@Component
@Data
public class Db2Properties {
    private String url;
    private String username;
    private String password;
    private String driverClassName;
}

Db1Config、Db2Config
以下两个是需要修改的配置信息

设置@EnableJpaRepositories注解里的basePackages属性配置jpa持久化类的包路径
设置db1EntityManagerFactory方法中的packages方法设置实体类所在的包路径

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "db1EntityManagerFactory",
        transactionManagerRef = "db1TransactionManager",
        //basePackages = {"com.zhubayi.jpademo.repository.user"})// 指定该数据源操作的DAO接口包
        basePackageClasses = UserRepository.class) //定一个要扫描包中的一个类或接口,将扫描所在包中的所有repository。 
public class Db1Config {

    @Autowired
    @Qualifier("db1DataSource")
    private DataSource db1DataSource;


    @PersistenceUnit(name = "db1PersistenceUnit")
    @Primary
    @Bean(name = "db1EntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean db1EntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(db1DataSource);
        em.setPersistenceUnitName("db1PersistenceUnit");
        em.setPackagesToScan(new String[]{"com.zhubayi.jpademo.entity"});

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        //开启sql展示
        vendorAdapter.setShowSql(true);
        em.setJpaVendorAdapter(vendorAdapter);
        return em;
    }

    @Primary
    @Bean(name = "db1TransactionManager")
    public PlatformTransactionManager db1TransactionManager(@Qualifier("db1EntityManagerFactory") EntityManagerFactory factory) {
        return new JpaTransactionManager(factory);
    }

}



@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "db2EntityManagerFactory",
        transactionManagerRef = "db2TransactionManager",
        //basePackages = {"com.zhubayi.jpademo.repository.address})// 指定该数据源操作的DAO接口包
        basePackageClasses = AddressRepository.class)//定一个要扫描包中的一个类或接口,将扫描所在包中的所有repository。 
public class Db2Config {

    @Autowired
    @Qualifier("db2DataSource")
    private DataSource db2DataSource;

    @PersistenceUnit(name  ="db2PersistenceUnit")
    @Bean(name = "db2EntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean db2EntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(db2DataSource);
        em.setPersistenceUnitName("db2PersistenceUnit");
        em.setPackagesToScan(new String[]{"com.zhubayi.jpademo.entity"});

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        //开启sql展示
        vendorAdapter.setShowSql(true);
        em.setJpaVendorAdapter(vendorAdapter);
        return em;
    }


    @Bean(name = "db2TransactionManager")
    public PlatformTransactionManager db2TransactionManager(@Qualifier("db2EntityManagerFactory") EntityManagerFactory factory) {
        return new JpaTransactionManager(factory);
    }
}

@EnableJpaRepositories注解说明
  value:basePackages的别名,简化basePackages。
  basePackages:用于配置扫描Repositories所在的包。填写字符串(或字符串数组)形式的包名。
  basePackageClassesbasePackages的安全替代选选项。指定一个要扫描包中的一个类或接口,将扫描所在包中的所有repository
    可以考虑在每个要扫描的包中创建一个类或接口,它除了被这个属性引用外,没有其他用途。
  includeFilters:指定哪些类型的组件被扫描。
  **excludeFilters**:指定哪些类型的组件不被扫描。
  repositoryImplementationPostfix:查找自定义存储库实现时要使用的后缀。默认为Impl。对于名为PersonRepository的存储库,将通过扫描PersonRepositoryImpl来查找相应的实现类。
  namedQueriesLocation:配置Spring-Data的named queries 属性文件的位置,默认META-INF/jpa-named-queries.properties。
  queryLookupStrategy:查询方法的查询策略。默认为QueryLookupStrategy.Key.CREATE_IF_NOT_FOUND。
  repositoryFactoryBeanClass:用于每个存储库实例的FactoryBean类。默认为JpaRepositoryFactoryBean。
  repositoryBaseClass:配置存储库基类,以用于为该特定配置创建存储库代理。
  entityManagerFactoryRef:配置EntityManagerFactory bean定义的名称。默认为entityManagerFactory。
  transactionManagerRef:配置PlatformTransactionManager bean定义的名称。默认为transactionManager。
  considerNestedRepositories:配置是否发现嵌套的Repository接口(如定义为内部类)。默认为false。
 enableDefaultTransactions:配置Spring-Data-Jpa 的Repositories是否启用默认事务,默认为true。如果禁用,则必须在外层使用。
  bootstrapMode:配置在引导生命周期中何时初始化Repository。默认为BootstrapMode.DEFAULT,除了添加了BootstrapMode.LAZY的接口外,其他接口立即初始化。
    BootstrapMode.LAZY,Repository的bean定义被认为是懒加载注入,并且只在首次使用时初始化,即应用程序可能在没有初始化Repository的情况下完全启动。
    BootstrapMode.DEFERRED,Repository的bean定义被认为是懒加载注入,但存储库初始化在应用程序上下文引导完成时触发。
  escapeCharacter:配置在包含contains、startsWith或endsWith子句的派生查询中用于转义 _ 或 % 的通配符字符。

3.目录结构

spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第4张图片
测试:
user测试:
根据配置文件application.yml得知usertestdemo数据库中。
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第5张图片
此时数据库没有数据。
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第6张图片
然后执行之前的userTest测试。
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第7张图片
插入成功!

address测试:
根据配置文件application.yml得知addressshop数据库中。
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第8张图片

此时数据库没有数据。
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第9张图片
然后执行之前的contextLoads测试。
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第10张图片

shop数据库查看
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第11张图片
插入成功!

jpa分页条件查询

首先在t_address数据库添加五条数据。
spring-boot-starter-data-jpa 配置多个数据源与jpa实体类继承的问题、分页条件查询_第12张图片
然后在Address实体类里重写oString方法。

@Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", county='" + county + '\'' +
                '}';
    }

然后进行测试

@Test
    void pageTest(){
        int pageNum=1;
        int pageSize=2;
        Specification<Address> specification =
                (root, criteriaQuery, criteriaBuilder) -> {
                    //Pridicate:表示一个查询条件
                    List<Predicate> predicates = new ArrayList<>();//创建一个条件集合
                        //获取属性
                        Path<String> rid = root.get("province");
                        //构造查询条件
                        predicates.add(criteriaBuilder.equal(rid,  "四川省"));

                    //必须使用toArray(T[])的有参数方法,因为cq.where(p)中的参数的类型必须是Predicate[]数组类型。
                    //toArray()无参返回的是一个Object类型。
                    //新建数组方式之一:new A[number]
                    return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                };
        Pageable pageable = PageRequest.of(pageNum - 1, pageSize, Sort.Direction.ASC, "rid");
        Page<Address> addressPage = addressRepository.findAll(specification, pageable);
        addressPage.forEach(System.out::println);
        System.out.println("总页数:"+addressPage.getTotalPages());
    }

输出:
在这里插入图片描述

你可能感兴趣的:(java,spring,java,数据库)