Spring Data之JPA/Hibernate的OneToOne示例

OneToOne场景

OneToOne主要使用在存在一一对应的业务场景下,例如将一个用户信息拆分为了2个部分:基本信息和扩展信息;在这种场景下,就需要进行OneToOne的映射使用。

演示场景

用户信息将被分拆为2个部分: 基本信息和扩展信息.
基本信息包括:id,name, location, 所购产品
扩展信息:level(用户级别), discountRate折扣率,remark备注等信息

他们之间是一一对应的关系,都从属于user这个实体类。

类定义

UserEntity.java类的定义:

/**
 * User DAO.
 * @author  xxx
 * @date 2019-05-04
 */
@EqualsAndHashCode(callSuper = true)
@Table(name="t_user")
@Entity
@Data
@EntityListeners(AuditingEntityListener.class)
public class UserEntity extends BaseEntity {
    @Column
    private String name;

    @Column
    private String location;

    /**
     * Who owns the foreign Key, who will be the owner, and declare the JoinColumn.
     *
     */
    @JsonManagedReference
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name="user_ext_id", referencedColumnName = "id")
    private UserExtEntity userExtEntity;
}

UserExtEntity.java用户扩展信息定义如下:

/**
 * 用户的扩展信息.
 * @author xxx
 * @date 2019/05/03
 *
 */
@Table(name="t_user_ext")
@Entity
@Data
@EntityListeners(AuditingEntityListener.class)
public class UserExtEntity extends BaseEntity {
    //用户级别
    @Column
    private Integer level;

    /**
     * 折扣率
     */
    @Column(name="discount_rate")
    private Float discountRate;

    /**
     * 用户备注
     */
    @Column
    private String remark;

    @JsonBackReference
    @OneToOne(cascade = {CascadeType.REFRESH},
            mappedBy = "userExtEntity", fetch = FetchType.LAZY)
    private UserEntity user;
}

DAO层声明

User DAO之UserRepsoitory.java:

/ * DAO
 *
 * @author: xxx
 * @date:  2019/05/03
 */
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
}

UserExt DAO之UserExtRepository.java:

/**
 *  User Ext Repository
 *
 * @author  xxx
 * @date 2019-05-05
 */
@Repository
public interface UserExtRepository extends JpaRepository<UserExtEntity, Long> {
}

BaseEntity.java 几类定义:

@Data
@MappedSuperclass
public class BaseEntity implements java.io.Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @CreatedDate
    @Column(name="created_time")
    private Date createdTime;

    @LastModifiedDate
    @Column(name="updated_time")
    private Date updatedTime;

    @CreatedBy
    @Column(name="created_by")
    private String createdBy;

    @LastModifiedBy
    @Column(name="last_modified_by")
    private String lastModifiedBy;

    @Version
    private Integer version;
}

封装结果数据类,ResultData.java:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResultData {
    private Integer code;
    private String message;
    private Object data;

    public static ResultData failure() {
        ResultData resultData = ResultData.builder().code(-1).message("failure").build();

        return resultData;
    }

    public static ResultData success() {
        ResultData resultData = ResultData.builder().code(0).message("success").build();

        return resultData;
    }
}

测试代码

Controller层的测试代码如下:

@RestController
@Slf4j
public class UserController {
   @Autowired
   private UserRepository userRepo;

   @Autowired
   private UserExtRepository extRepo;

   @GetMapping("/case1")
   public ResultData testCreate() {
       UserEntity user = new UserEntity();
       user.setName("zhangsan");
       user.setLocation("BeiJing");

       UserExtEntity userExtEntity = new UserExtEntity();
       userExtEntity.setDiscountRate(0.9f);
       userExtEntity.setLevel(1);
       userExtEntity.setRemark("remark info");

       user.setUserExtEntity(userExtEntity);

       user = this.userRepo.save(user);

       log.info("User:{}", user);

       ResultData resultData = ResultData.success();
       resultData.setData(user);

       return resultData;
   }
}

在postman中直接调用这些链接进行测试,然后进入到数据库中进行数据检查数据是否存在。

分析说明

@OneToOne之主从关系

其表示1对1的数据对应关系,主要是基于表的数据关系。
在@OneToOne注解的映射关系中,如果需要定义双向映射关系,其中定义了外键约束的表中外键字段,其所对应的Entity属性为hosted(主),其另外Entity类中定义为从属关系。
在本示例中,将user_ext_id定义在了UserEntity对应的t_user表中,则UserEntity中的userExtEntity字段为hosted(主),在UserExtEntity定义的user属性则为从属。
从另外一个维度来观察,定义了JoinColumn的字段为hosted字段,host字段与JoinColumn是紧密相关的。

@OneToOne之属性

cascade属性定义了数据操作之时的同步类型,其涵盖了CURD以及游离态:

public enum CascadeType { 

    /** Cascade all operations */
    ALL, 

    /** Cascade persist operation */
    PERSIST, 

    /** Cascade merge operation */
    MERGE, 

    /** Cascade remove operation */
    REMOVE,

    /** Cascade refresh operation */
    REFRESH,

    /**
     * Cascade detach operation
     *
     * @since Java Persistence 2.0
     * 
     */   
    DETACH
}

fetch其表示当前Entity的数据是否提前加载,eager表示其预加载,在Entity创建之时,即从数据库中拉取加载。Lazy表示只有在真正访问该数据字段时,才从数据库中进行加载拉取。

public enum FetchType {
    /** Defines that data can be lazily fetched. */
    LAZY,

    /** Defines that data must be eagerly fetched. */
    EAGER
}

mappedBy字段表示当前字段为ORM映射中的从属字段,其从属于mappedBy中的值。在示例中是userExtEntity,这个属性定义在UserEntity中。

Json转换中的循环依赖

由于在ORM中的双向依赖会存在循环依赖的关系,所以在进行结果输出之时,会出现无限的循环直至异常报错,对于此类问题,该如何处理呢?
基于@JsonManagedReference,@JsonBackReference进行Json双向映射中的循环依赖管理,解决映射的问题。
@JsonManagedReference is the forward part of reference – the one that gets serialized normally. 前项部分,用于正常的序列化操作。
@JsonBackReference is the back part of reference – it will be omitted from serialization.
后项部分,这部分的内容将在序列化中被忽略。

总结

在本文中,通过一个相对完成的示例展示了对于OneToOne映射的基本用法,在其中包含了@OneToOne、@JoinColumn、@JsonManagedReference和@JsonBackReference的用法,大家多多体会其中用法。

参考资料

  1. Json循环依赖的解决
  2. JPA OneToOne
发布了478 篇原创文章 · 获赞 803 · 访问量 433万+

猜你喜欢

转载自blog.csdn.net/blueheart20/article/details/89814769