1

问题描述

有些场景下,有些关系不足以拆分出一个实体,但是如果新建一个对象管理会让代码更清晰,这种场景下用到了内嵌对象。

clipboard.png

一个Teacher,三个属性,idfirstNamelastName

我们可以直接这么写,也可以建一个内嵌的Name对象,虽然在一个实体中差别不大,但是如果以后有用到name的场景,可以直接使用该内嵌对象。

解决方案

实现

以下代码省略setget方法:

@Entity
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private Name name;
}
@Embeddable
public class Name {

    private String firstName;

    private String lastName;
}

启动程序,生成的表结构如下:

clipboard.png

所以,映射时直接将内嵌对象中的字段添加到该实体中,然后映射到数据表。

思考

其实说白了就是许多的实体中都有这几个属性,但是根据逻辑关系或复杂度来说不足以单独建立一个实体,所以使用内嵌对象。

就像项目中的不确定度与测量范围一样,虽然属性不少,不足以单独抽出一个实体,但是又有很多实体去用。

根据内嵌的原理,我们可以推断,使用内嵌对象的实体与内嵌对象是一对一的关系时,肯定是可以映射的?那如果映射一个List呢?因为实际业务场景是不准确度,之前都是一对一使用的该内嵌对象,然后需求变动,需要一对多实现。

尝试多关联

抱着怀疑的态度,我们尝试一下。

@Entity
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private List<Name> names = new ArrayList<>();
}

运行程序,果然报错,应该是我们映射的方式有问题,去查查官方文档。

clipboard.png

官方文档

官方文档自然是万能的:Collection Mapping - Hibernate

clipboard.png

Using annotations you can map Collections, Lists, Maps and Sets of associated entities using @OneToMany and @ManyToMany. For collections of a basic or embeddable type use @ElementCollection.
使用注解映射`Collection`、`List`、`Map`、`Set`,实体使用`@OneToMany`与`@ManyToMany`,基本类型或内嵌类型使用`@ElementCollection`。
@Entity
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ElementCollection
    private List<Name> names = new ArrayList<>();
}

运行程序,查看生成的表结构:

clipboard.png

teacher表:

clipboard.png

teacher_names表:

clipboard.png

看着似乎没什么问题,但是设计表会发现teacher_names是有猫腻的。

clipboard.png

内嵌对象集合映射的表没有主键!当然,如果仅仅从teacher这方面看,这么设计是很合理的。但是并不能满足我们的需求。

思考

StackOverflow回答的非常好。

clipboard.png

内嵌对象没有id,如果需求复杂,还是用实体吧!内嵌对象只适用于该对象十分简单的情况。

兼容原内嵌

所以,现在的需求变成了原内嵌对象不能动,好多实体用到了,然后又需要将内嵌对象变为实体。原问题就是因为内嵌对象没有id造成的,填个id就好了。

@Entity
public class SuperName {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private Name name;

    @ManyToOne
    private Teacher teacher;
}
@Entity
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(mappedBy = "teacher")
    private List<SuperName> superNames = new ArrayList<>();
}

再看表结构,没毛病!

clipboard.png

clipboard.png

总结

用一个新的注解或编码方式时,首要任务是新建一个项目,然后去各种尝试学会它的应用场景,看别人的博客永远没有自己尝试一遍理会的透彻。

只要没有被官方弃用的东西,都有其存在的价值,只是应用场景不同而已,永远没有无用的东西,仅仅是因为我们还没发现。


张喜硕
2.1k 声望423 粉丝

浅梦辄止,书墨未浓。


引用和评论

0 条评论