Spring Data JPA实践与学习(八)

联合主键

在实际的工作中,我们会经常遇到联合主键的情况。那么JPA如何实现呢?
1、通过 @IdClass 做到联合主键。
样例:
第一步:新建一个 UserInfoID 类里面是联合主键。

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserInfoID implements Serializable {
   private String name,telephone;
}

第二步:再新建一个 UserInfo 的实体,采用 @IdClass 引用联合主键类。

@Entity
@Data
@Builder
@IdClass(UserInfoID.class)
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
   private Integer ages;
   @Id
   private String name;
   @Id
   private String telephone;
}

第三步:新增一个 UserInfoReposito 类来做 CRUD 操作。

public interface UserInfoRepository extends JpaRepository<UserInfo,UserInfoID> {
}

第四步:写一个测试用例,测试一下。

@DataJpaTest
public class UserInfoRepositoryTest {
   @Autowired
   private UserInfoRepository userInfoRepository;
   @Test
   public void testIdClass() {
   userInfoRepository.save(UserInfo.builder().ages(1).name("jack").telephone("123456789").build());
      Optional<UserInfo> userInfo = userInfoRepository.findById(UserInfoID.builder().name("jack").telephone("123456789").build());
      System.out.println(userInfo.get());
   }
}

测试程序启动后可以在控制台看到:

Hibernate: create table user_info (name varchar(255) not null, telephone varchar(255) not null, ages integer, primary key (name, telephone))
Hibernate: select userinfo0_.name as name1_3_0_, userinfo0_.telephone as telephon2_3_0_, userinfo0_.ages as ages3_3_0_ from user_info userinfo0_ where userinfo0_.name=? and userinfo0_.telephone=?
UserInfo(ages=1, name=jack, telephone=123456789)

2、@Embeddable 与 @EmbeddedId 注解使用
第一步:在我们上面例子中的 UserInfoID 里面添加 @Embeddable 注解。

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Embeddable
public class UserInfoID implements Serializable {
   private String name,telephone;
}

第二步:改一下我们刚才的 User 对象,删除 @IdClass,添加 @EmbeddedId 注解,如下:

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
   private Integer ages;
   @EmbeddedId
   private UserInfoID userInfoID;
   @Column(unique = true)
   private String uniqueNumber;
}

第三步:UserInfoRepository 不变,我们直接修改一下测试用例。

@Test
public void testIdClass() {
  userInfoRepository.save(UserInfo.builder().ages(1).userInfoID(UserInfoID.builder().name("jack").telephone("123456789").build()).build());
   Optional<UserInfo> userInfo = userInfoRepository.findById(UserInfoID.builder().name("jack").telephone("123456789").build());
   System.out.println(userInfo.get());
}

运行完之后,你可以得到相同的结果。那么 @IdClass 和 @EmbeddedId 的区别是什么?有以下两个方面:

如上面测试用例,在使用的时候,Embedded 用的是对象,而 IdClass 用的是具体的某一个字段;
二者的JPQL 也会不一样:
① 用 @IdClass JPQL 的写法:SELECT u.name FROM UserInfo u
② 用 @EmbeddedId 的 JPQL 的写法:select u.userInfoId.name FROM UserInfo u

实体之间的继承关系如何实现?

在 Java 面向对象的语言环境中,@Entity 之间的关系多种多样,而根据 JPA 的规范,我们大致可以将其分为以下几种:

1、 纯粹的继承,和表没关系,对象之间的字段共享。利用注解 @MappedSuperclass,协议规定父类不能是 @Entity。
2、单表多态问题,同一张 Table,表示了不同的对象,通过一个字段来进行区分。利用@Inheritance(strategy = InheritanceType.SINGLE_TABLE)注解完成,只有父类有 @Table。
3、多表多态,每一个子类一张表,父类的表拥有所有公用字段。通过@Inheritance(strategy = InheritanceType.JOINED)注解完成,父类和子类都是表,有公用的字段在父表里面。
4、Object 的继承,数据库里面每一张表是分开的,相互独立不受影响。通过@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)注解完成,父类(可以是一张表,也可以不是)和子类都是表,相互之间没有关系。

1、@MappedSuperclass 注解
它主要是用来解决公共 BaseEntity 的问题,而且其代表的是继承它的每一个类都是一个独立的表。

@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MappedSuperclass {
}

2、@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
父类实体对象与各个子实体对象共用一张表,通过一个字段的不同值代表不同的对象,我们看一个例子。

我们抽象一个 Book 对象,如下所示:

@Entity(name="book")
@Data
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="color", discriminatorType = DiscriminatorType.STRING)
public class Book {
   @Id
   @GeneratedValue(strategy= GenerationType.AUTO)
   private Long id;
   private String title;
}

再新建一个 BlueBook 对象,作为 Book 的子对象。

@Entity
@Data
@EqualsAndHashCode(callSuper=false)
@DiscriminatorValue("blue")
public class BlueBook extends Book{
   private String blueMark;
}

再新建一个 RedBook 对象,作为 Book 的另一子对象。

@Entity
@DiscriminatorValue("red")
@Data
@EqualsAndHashCode(callSuper=false)
public class RedBook extends Book {
   private String redMark;
}

这时,我们一共新建了三个 Entity 对象,其实都是指 book 这一张表,通过 book 表里面的 color 字段来区分红书还是绿书。我们继续做一下测试看看结果。

public interface RedBookRepository extends JpaRepository<RedBook,Long>{
}
@RestController
@RequestMapping("/book")
public class BookController {
        @Autowired
        private RedBookRepository redBookRepository;
        @RequestMapping("/test1")
        public void testRedBook() {
            RedBook redBook = new RedBook();
            redBook.setTitle("redbook");
            redBook.setRedMark("redmark");
            redBook.setId(1L);
            redBookRepository.saveAndFlush(redBook);
            RedBook r = redBookRepository.findById(1L).get();
            System.out.println(r.getId()+":"+r.getTitle()+":"+r.getRedMark());
    }
}

通过postman发送请求,可以看到结果:

1:redbook:redmark

结果完全和预期的一样,这说明了 RedBook、BlueBook、Book,都是一张表,通过字段 color 的值不一样,来区分不同的实体。

3、@Inheritance(strategy = InheritanceType.JOINED)
4、@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

3、4两种方式,都是通过改变 Inheritance 策略,需要的话可以自行百度解决。和第二种差不多,所以就不列举了。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值