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:对应数据库中的列名
- mybatis
直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。
1.2 查询编写示例
- 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根据方法名称进行解析后查询。
- 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),与人物表相关联。
- 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
- Mybatis
直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。
2.2 查询编写示例
- hibernate
正常查询,因为设置了级联查询,会直接查询出来,OneToOne和ManyToOne默认不是延迟加载,所以不用设置就是及时加载.
- 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 实体编写示例
- 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:对查询的记录进行按字段排序。
- 默认是延迟加载。
- mybatis
直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了hibernate也能使用最好按上面的方式。
3.2 查询编写示例
- hibernate
正常查询,因为设置了级联查询,会直接查询出来,OneToMany默认是延迟加载
- Mybatis
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: 用户组与权限的关系,一个用户组可以拥有多个权限,一个权限又可包含多个用户组。

4.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; }
|
- mybatis
直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。
4.1.2 查询示例编写
hibernate
正常查询,因为设置了级联查询,会直接查询出来,(查询不同的Dao就会对应生成不同的实体)。
mybatis
- 返回model设置对应的 association 或 collection
- 正常sql语句的链表查询
4.2 两张表与关系表相对各自独立,没有完整级联
例2: 例如在translator中的,译员的母语和语言tag之间是多对多关系,tag是数据字典,初始化值后便不会改变。因为语言tag不仅母语需要使用,擅长语言这个属性也需要使用,所以中间表需要加字段“tag类型”用以区分是tag是被谁在使用。

4.2.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; ……
|
- mybatis
直接使用上面JPA的数据模型就可以,也可以不加上面的所有注解,为了JPA也能使用最好按上面的方式。
4.2.2 查询示例编写
hibernate
正常查询,因为设置了级联查询,会直接查询出来,(查询不同的Dao就会对应生成不同的实体)。
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 查询示例编写
- JPA
正常进行crud操作,JPA会自动对关联的表以及中间表进行相关操作。
- mybatis
正常sql语句的链表查询。
4.4 建议
- 此类查询建议使用mybatis完成,使用JPA设置关联关系存在限制性,又容易出现循环关联,查询时不灵活等问题。
- 多对多关系的解除由关系维护端来完成,关系被维护端不能解除关系,但是此处需要注意,不建议用级联修改关系。
- 多对多关系可以不做表间的外键,而是对单独的表进行增删改查,再维护对应关系表。