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
测试:
@Autowired
private AddressRepository addressRepository;
@Test
void contextLoads() {
Address address = new Address();
address.setRid(1L);
address.setProvince("四川省");
address.setCity("成都市");
address.setCounty("金牛区");
addressRepository.save(address);
}
@Autowired
private UserRepository userRepository;
@Test
void userTest(){
User user = new User();
user.setRid(1L);
user.setUserName("祝八一");
user.setEmail("123@");
user.setPhone("1878265xxxx");
userRepository.save(user);
}
可见在baseEntity
里面的rid也插入进了数据库
jpa配置多数据源
1.首先把application.yml
里面的数据源的配置先注释了。
换成如下的配置
spring:
datasource:
db1:
#数据库用户名
username: root
#数据库密码
password: 123456
#mysql数据库驱动程序(重要)
driverClassName: com.mysql.cj.jdbc.Driver
db2:
#数据库用户名
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
所在的包。填写字符串(或字符串数组)形式的包名。
basePackageClasses
:basePackages的安全替代选选项。指定一个要扫描包中的一个类或接口,将扫描所在包中的所有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
得知user
在testdemo
数据库中。
此时数据库没有数据。
然后执行之前的userTest
测试。
插入成功!
address测试:
根据配置文件application.yml
得知address
在shop
数据库中。
此时数据库没有数据。
然后执行之前的contextLoads
测试。
去shop
数据库查看
插入成功!
jpa分页条件查询
首先在t_address数据库添加五条数据。
然后在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());
}
输出: