• 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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

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

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

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

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

    配置数据库连接在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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试:

        @Autowired
        private AddressRepository addressRepository;
        @Test
        void contextLoads() {
            Address address = new Address();
            address.setRid(1L);
            address.setProvince("四川省");
            address.setCity("成都市");
            address.setCounty("金牛区");
            addressRepository.save(address);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

        @Autowired
        private UserRepository userRepository;
        @Test
        void userTest(){
            User user = new User();
            user.setRid(1L);
            user.setUserName("祝八一");
            user.setEmail("123@qq.com");
            user.setPhone("1878265xxxx");
            userRepository.save(user);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    可见在baseEntity里面的rid也插入进了数据库

    jpa配置多数据源

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

    在这里插入图片描述
    换成如下的配置

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    @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.目录结构

    在这里插入图片描述
    测试:
    user测试:
    根据配置文件application.yml得知usertestdemo数据库中。
    在这里插入图片描述
    此时数据库没有数据。
    在这里插入图片描述
    然后执行之前的userTest测试。
    在这里插入图片描述
    插入成功!

    address测试:
    根据配置文件application.yml得知addressshop数据库中。
    在这里插入图片描述

    此时数据库没有数据。
    在这里插入图片描述
    然后执行之前的contextLoads测试。
    在这里插入图片描述

    shop数据库查看
    在这里插入图片描述
    插入成功!

    jpa分页条件查询

    首先在t_address数据库添加五条数据。
    在这里插入图片描述
    然后在Address实体类里重写oString方法。

    @Override
        public String toString() {
            return "Address{" +
                    "province='" + province + '\'' +
                    ", city='" + city + '\'' +
                    ", county='" + county + '\'' +
                    '}';
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后进行测试

    @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());
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

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

  • 相关阅读:
    docker 操作redis
    数据挖掘学习——聚类分析(k-均值聚类、DBSCAN、AGNES)、python代码
    Jenkins kubernetes(k8s)滚动发布实战
    redis 集群(cluster)
    CSS 中的 HSL 和 HSLA 与 RGB 和 RGBA
    Pytroch 常见问题总结
    Spring Boot 最流行的 16 条实践,Java 开发变得更加简单!
    单片机-控制按键点亮LED灯
    Kubernetes入门到精通-基础知识
    自动化测试中对数据恢复的思考与实际业务改造实践
  • 原文地址:https://blog.csdn.net/qq_44732146/article/details/126800551