1. 实体中没有引用对象

1.1 实体编写实例

  1. hibernate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
@Table(name = "translator_info_change_log")
@NamedQuery(name = "TranslatorInfoChangeLog.findAll", query = "SELECT o FROM TranslatorInfoChangeLog o")
public class TranslatorInfoChangeLog extends AbstractModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "translator_id")
private Long translatorId;
@Column(name = "details")
private String details;
@Enumerated(EnumType.ORDINAL)
@Column(name = "account_status")
}

说明:

  • @Entity:标志这是一个数据库实体(pojo)
  • @Table:对应数据库的表名
  • @Id:标识此列是数据库中的主键
  • @GeneratedValue:数据库主键的生成方式,JPA提供的四种标准用法为TABLE(使用一个特定的数据库表格来保存主键),SEQUENCE(根据底层数据库的序列来生成主键,条件是数据库支持序列),IDENTITY(主键由数据库自动生成(主要是自动增长型)),AUTO(主键由程序控制)
  • @Column:对应数据库中的列名
  1. mybatis
    直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。

1.2 查询编写示例

  1. hibernate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface TranslatorInfoChangeLogRepository extends JpaRepository<TranslatorInfoChangeLog, Long>, JpaSpecificationExecutor<TranslatorInfoChangeLog> {
// 使用sql语句查询
// @Query(value = "select * from translator_info_change_log t where t.id = ?1", nativeQuery = true)
// 使用hql语句查询
@Query(value = "select t from TranslatorInfoChangeLog where t.id = ?1")
TranslatorInfoChangeLog findChangeLog(Long id);
@Modifying
@Query("update TranslatorInfoChangeLog p set status=0 where p.id in :ids")
void deleteTranslatorInfo(@Param("ids") List<Long> ids);
TranslatorInfoChangeLog findById(Long id);
}

说明:

  • 需要继承JpaRepository、JpaSpecificationExecutor
  • 这里只能返回本实体及其父类
  • findById转化成的SQL语句为:select * from modify_log where id = ?
  • @Modifying:标识是个更改操作
  • @Query:要执行的SQL语句,这不是原生的SQL,可以设置参数(nativeQuery = true),这就可以写原生SQL
  • 示例中的findChangeLog和findById作用是一样的,findChangeLog是用sql语句进行查询,findById是jpa根据方法名称进行解析后查询。
  1. mybatis
  • 编写查询的接口
  • 直接在查询接口对应的xml文件写sql语句
1
List<TranslatorInfoChangeLog> queryModifyLog(@Param("id") Long id);
1
2
3
<select id="queryModifyLog" resultType="TranslatorInfoChangeLog" patameterType="Long">
select * from translator_info_change_log where id = ?
</select>

其中查询语句写在

1.3 建议

此类查询建议使用JPA完成,开发速度快,只需按规则定义方法名就可以根据不同的字段进行查询,而用mybatis需要编写不同的SQL语句。

2. 实体中存在单实体引用对象(一对一或者多对一)

2.1 实体编写实例(以多对一为例)

台词和人物信息的关系,多个台词可能都属于同一个人物的。在多的一端(台词表)添加外键(人物的id),与人物表相关联。

  1. hibernate
1
2
3
@ManyToOne(cascade = CascadeType.REFRESH, targetEntity = Character.class, optional = true)
@JoinColumn(name = "character_person_id")
private Character character;

其中:

  • @ManyToOne:标识关联关系,cascade标识相关级联操作,其中CascadeType.PERSIST:级联新增(又称级联保存,CascadeType.MERGE:级联合并(级联更新),CascadeType.REMOVE:级联删除,CascadeType.REFRESH:级联刷新,CascadeType.ALL:以上四种都是。
  • targetEntity 定义关系类的类型,默认是该成员属性对应的类类型,所以通常不需要提供定义。
  • optional标识是否可为空。
  • @JoinColumn:标识用于关联的字段
    @ ManyToOne和OneToOne的fetch 属性默认值是FetchType.EAGER
  1. Mybatis

直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。

2.2 查询编写示例

  1. hibernate

正常查询,因为设置了级联查询,会直接查询出来,OneToOne和ManyToOne默认不是延迟加载,所以不用设置就是及时加载.

  1. mybatis
  • 返回模型设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
……
<association property="character" column="character_person_id" javaType="com.star.ott.scriptsTranslation.domain.Character">
<id property="id" column="c_id"/>
<result property="name" column="name"/>
<result property="englishName" column="english_name"/>
<result property="age" column="age"/>
<result property="sex" column="sexjavaType="com.star.ott.scriptsTranslation.domain.enums.Sex"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandl"/>
<result property="remarks" column="remarks"/>
<result property="characterType" column="character_type"
javaType="com.star.ott.scriptsTranslation.domain.enums.CracterType"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandl"/>
<result property="dramaId" column="dramaId"/>
</association>
……
  • 查询语言需要做关联
1
2
3
4
5
<select id="" resultMap="">
……
LEFT JOIN character_person c ON c.id = l.character_person_id AND c.delete_flag = 1
……
</select>
  • 一对一和多对一,实体映射用标签。

2.3 建议

此类查询建议使用JPA完成,开发速度快,只需要设置实体的注解,mybatis需要join语句,还需要设置返回的resultMap。

3. 实体中存在多实体引用对象(一对多)

例如剧集下存在多个台词,剧集是“一”的一方,台词是“多”的一方,在多的一端(台词表)添加外键(剧集的id),与剧集表相关联。

3.1 实体编写示例

  1. hibernate
1
2
3
4
5
6
7
……
@OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY)
@JoinColumn(name="episodeid")
@Where(clause = "delete_flag = 1")
@OrderBy(value = "id asc")
private List<Line> lines;
……

说明:

  • @OneToMany:标识关联关系,Cascade标识相关级联操作,fetch标识加载方式是懒加载还是及时加载。
  • @Where:过滤条件,只查询符合条件的级联表里的记录。
  • @OrderBy:对查询的记录进行按字段排序。
  • 默认是延迟加载。
  1. mybatis

直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了hibernate也能使用最好按上面的方式。

3.2 查询编写示例

  1. hibernate

正常查询,因为设置了级联查询,会直接查询出来,OneToMany默认是延迟加载

  1. Mybatis
  • 返回model设置
1
2
3
4
5
6
7
8
9
10
11
……
<collection property="sourceLanguages" column="line_id" ofType="com.star.ott.scriptsTranslation.domain.TranslationLine">
<id property="id" column="tl_id"/>
<result property="translationTaskId" column="translation_task_id"/>
<result property="lineId" column="line_id"/>
<result property="lineStr" column="line_str"/>
<result property="language" column="language"
javaType="com.star.ott.scriptsTranslation.domain.enums.Language"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
</collection>
……
  • 查询语言需要做关联
1
2
3
4
5
<select id="" resultMap="">
……
LEFT JOIN translation_line tl ON tl.line_id = l.id AND tl.delete_flag = 1 AND tl.translation_task_id IS NULL
……
</select>
  • 一对多,实体映射用标签。
  • collection和association标签的实体映射需要放在一起,不能交叉放置,否则会出现错误。

3.3 建议

此类查询建议使用hibernate完成,开发速度快,只需要设置实体的注解,mybatis需要join语句,还需要设置返回的resultMap但是执行效率上mybatis会更好一些,如果数据量大,就需要测试后选择。

4. 两个实体间存在中间实体关联(多对多)

4.1 两张表与关系表OneToMany级联,关系表与两张表ManyToOne级联。

例1: 用户组与权限的关系,一个用户组可以拥有多个权限,一个权限又可包含多个用户组。

image

4.1.1 实体编写示例
  1. hibernate

Role实体

1
2
3
4
5
6
7
8
9
10
11
@Entity
@Table(name="glw_role")
public class Role{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
private Long id;
@Column(length=50)
private String name;
@OneToMany(mappedBy="role",cascade=CascadeType.ALL)
private Set<RoleResource> roleResources = new HashSet<RoleResource>();
}

Resource实体

1
2
3
4
5
6
7
8
9
10
11
@Entity
@Table(name="glw_resource")
public class Resource{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
private Long id;
@Column(length=50)
private String name;
@OneToMany(mappedBy="resource",cascade=CascadeType.ALL)
private Set<RoleResource> roleResources = new HashSet<RoleResource>();
}

RoleResource辅助实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@Table(name="glw_role_resource")
public class Resource{
@Id
@GeneratedValue(strategy=GenerationType.TABLE)
private Long id;
@Column
private Integer sort;
@ManyToOne(cascade=Cascade.ALL)
@JoinColumn(name="roleId",nullable=true)
private Role role;
@ManyToOne(cascade=Cascade.ALL)
@JoinColumn(name="resourceId",nullable=true)
private Resource resource;
}
  1. mybatis
    直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。
4.1.2 查询示例编写
  1. hibernate
    正常查询,因为设置了级联查询,会直接查询出来,(查询不同的Dao就会对应生成不同的实体)。

  2. mybatis

  • 返回model设置对应的 association 或 collection
  • 正常sql语句的链表查询

4.2 两张表与关系表相对各自独立,没有完整级联

例2: 例如在translator中的,译员的母语和语言tag之间是多对多关系,tag是数据字典,初始化值后便不会改变。因为语言tag不仅母语需要使用,擅长语言这个属性也需要使用,所以中间表需要加字段“tag类型”用以区分是tag是被谁在使用。

image

4.2.1 实体编写示例
  1. hibernate

translator和中间表之间一对多级联,中间表和数据字典之间不做级联,可以由translator对中间表进行crud操作,中间表和数据字典之间的关系需要手动维护。

translator

1
2
3
4
5
6
……
@OneToMany(cascade = {CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "translator_id")
@Where(clause = "delete_flag = 1 and dict_type = 2")
private Set<TranslatorDictRelation> motherTongue;
……

tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
……
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 数据字典类型
*/
@Enumerated(EnumType.ORDINAL)
@Column(name = "dict_type")
private DictType dictType;
/**
* 标签号
*/
@Column(name = "tag_num")
private Integer tagNum;
/**
* 标签值
*/
@Column(name = "tag_value")
private String tagValue;
……

TranslatorDictRelation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
……
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 译员id
*/
@Column(name = "translator_id")
private Long translatorId;
/**
* tag id
*/
@Column(name = "dict_id")
private Long dictId;
/**
* 数据字典类型
*/
@Enumerated(EnumType.ORDINAL)
@Column(name = "dict_type")
private DictType dictType;
……
  1. mybatis

直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。

4.2.2 查询示例编写
  1. hibernate
    正常查询,因为设置了级联查询,会直接查询出来,(查询不同的Dao就会对应生成不同的实体)。

  2. mybatis

  • 返回model设置对应的 association 或 collection
  • 正常sql语句的链表查询

4.3 两张表用ManyToMany级联,实体映射上不与关系表级联

例3: 两张多对多表关系均可能出现crud操作,中间表除了作为外键的字段和唯一id,没有其他字段。

4.3.1 实体编写示例

TranslatorTest

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
@Entity
@Table(name = "translator_test")
@NamedQuery(name = "TranslatorTest.findAll", query = "SELECT o FROM TranslatorTest o")
public class TranslatorTest {
/**
* 主键
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 证件号
*/
@Column(name = "credential_number")
private String credentialNumber;
/**
* 空闲时段
*/
@ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
@JoinTable(name = "translator_dict_relation_test",
joinColumns = {@JoinColumn(name = "translator_id")}, // 由哪端进行维护
inverseJoinColumns = {@JoinColumn(name = "dict_id")}) // 可由两张表进行维护关系和查询,用不同的dao就行
@OrderBy("id")
private Set<TagTest> freeTime = new HashSet<>();
……
}

TagTest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity
@Table(name = "tag_test")
@NamedQuery(name = "TagTest.findAll", query = "SELECT o FROM TagTest o")
public class TagTest {
/**
* 主键
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 标签值
*/
@Column(name = "tag_value")
private String tagValue;
@ManyToMany(mappedBy = "freeTime", cascade = CascadeType.ALL) // 映射的字段
private Set<TranslatorTest> translatorTestSet = new HashSet<>();
……
}

中间关系表不需要映射实体,但是需要创建出来:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE `translator_dict_relation_test`(
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '标识',
`translator_id` bigint(20) COMMENT '译员项标识',
`dict_id` bigint(20) COMMENT '数据字典标识',
PRIMARY KEY (`id`),
KEY `FK_translator_dict_id` (`translator_id`),
KEY `FK_dict_translator_id` (`dict_id`),
CONSTRAINT `FK_translator_dict_id` FOREIGN KEY (`translator_id`) REFERENCES `translator_test` (`id`),
CONSTRAINT `FK_dict_translator_id` FOREIGN KEY (`dict_id`) REFERENCES `tag_test` (`id`)
)ENGINE=InnoDB
COMMENT = 'translator_dict_relation_test'
CHARACTER SET utf8 COLLATE utf8_general_ci;
4.3.2 查询示例编写
  1. JPA
    正常进行crud操作,JPA会自动对关联的表以及中间表进行相关操作。
  2. mybatis
    正常sql语句的链表查询。

4.4 建议

  • 此类查询建议使用mybatis完成,使用JPA设置关联关系存在限制性,又容易出现循环关联,查询时不灵活等问题。
  • 多对多关系的解除由关系维护端来完成,关系被维护端不能解除关系,但是此处需要注意,不建议用级联修改关系。
  • 多对多关系可以不做表间的外键,而是对单独的表进行增删改查,再维护对应关系表。