常用注解
- @Entity实例常用注解
- 常用注解
- @Id
- @IdClass
- 关联关系注解
- @OneToMany一对多和@ManyToOne 多对一
- 关联查询Left join、Inner join 与 @EntityGraph
- @EntityGraph
- 例子
- Dao层
- Entity层
@Entity实例常用注解
常用注解
@Id
定义属性为数据库的主键,一个实体里面必须有一个,并且必须和 @GeneratedValue 配合使用和成对出现。
@IdClass
利用外部类的联合主键,源码:
public @interface IdClass {
//联合主键的类
Class value();
}
作为符合主键类:
- 必须实现 Serializable 接口
- 必须有默认的 public 无参数的构造方法
- 必须覆盖 equals 和 hashCode 方法。equals 方法用于判断两个对象是否相同,EntityManger 通过 find 方法来查找 Entity 时,是根据 equals 的返回值来判断的
关联关系注解
@OneToMany一对多和@ManyToOne 多对一
public @interface OneToMany {
Class targetEntity() default void.class;
//cascade 级联操作策略:(CascadeType.PERSIST、CascadeType.REMOVE、CascadeType.REFRESH、CascadeType.MERGE、CascadeType.ALL)
如果不填,默认关系表不会产生任何影响。
CascadeType[] cascade() default {};
//数据获取方式EAGER(立即加载)/LAZY(延迟加载)
FetchType fetch() default LAZY;
//关系被谁维护,单项的。注意:只有关系维护方才能操作两者的关系。
String mappedBy() default "";
//是否级联删除。和CascadeType.REMOVE的效果一样。两种配置了一个就会自动级联删除
boolean orphanRemoval() default false;
}
public @interface ManyToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
boolean optional() default true;
}
关联查询Left join、Inner join 与 @EntityGraph
- Left join & Inner join 问题
当使用 @ManyToMany、@ManyToOne、@OneToMany、@OneToOne 关联关系的时候,SQL 真正执行的时候都是由一条主表查询和 N 条子表查询组成的。这种查询效率一般比较低下,子对象有多少个就会执行 N+1 条 SQL。
有时候我们需要用到 Left Join 或者 Inner Join 来查询来提高效率,只能通过 @Query 的 JQPL 语法去实现Spring Data JPA 为了简单的提高查询率,引入了EntityGraph 的概念,可以解决 N+1 条 SQL 的问题
@EntityGraph
JPA 2.1 推出来的 @EntityGraph、 @NamedEntityGraph 用来提高查询效率,很好的解决了 N+1 条 SQL 的问题,两者需要配合起来使用,缺一不可。@NamedEntityGraph 配置在 @Entity 上面,而 @EntityGraph 配置在 Repository 的查询方法上面,但是我使用左外连接条件测试,使用这个注解执行查询时查询时间变长了
步骤如例子中所示:
- 先在 Entity 里面定义 @NamedEntityGraph,其他都不变,其中 @NamedAttributeNode 可以有多个,也可以有一个。
subgraphs:子图,举例子:假如Persion类entity里有private List user属性,被@OneToMany注解,但是User里有个字段Address addr,那么subgraphs里就是配置Address的信息,就是嵌套的意思
@NamedEntityGraph可以命名多组,并且可以通过subgraphs来控制字段要不要输出,
@Repeatable(NamedEntityGraphs.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedEntityGraph {
//自己起一个名字,作为此定义的唯一标识,这样可以定义多组设置,根据这个名字来决定使用哪个,后面在dao层会用到
String name() default "";
//属性节点,就是Entity里被@OneToMany注解的属性
NamedAttributeNode[] attributeNodes() default {};
boolean includeAllAttributes() default false;
//子图
NamedSubgraph[] subgraphs() default {};
//子类子图
NamedSubgraph[] subclassSubgraphs() default {};
}
//子图里的字段你会输出
subgraphs = {
@NamedSubgraph(name = "",attributeNodes = {
@NamedAttributeNode(value = "",subgraph = "123")
})
//子图字段会输出null
subgraphs = {
@NamedSubgraph(name = "country",attributeNodes = {})
}
- 只需要在查询方法上加 @EntityGraph 注解即可,其中 value 就是 @NamedEntityGraph 中的 Name,在实例配置看例子。
例子
1.这里我建立两张表,user_relation表(表1),bd_copy表(表2),表1与表二通过peijectid字段具有一对多关系,第一次使用立即加载,返回结果没有问题,加载速度较慢,直接返回
2. 换成延迟加载,发现dao层查询结果不经过处理直接返回给controller层会出现异常,如下
3. 解决:除了eager,还没找到有效方法
延迟加载出现的异常:
failed to lazily initialize a collection of role: com.example.springboot.JPA.entity.UserCrawlRelationEntity.bdcTaskCopyEntityList, could not initialize proxy - no Session
failed to lazily initialize a collection of role: com.example.springboot.JPA.entity.UserCrawlRelationEntity.bdcTaskCopyEntityList, could not initialize proxy - no Session
未能延迟初始化角色集合:com.example.springboot.JPA.entity.UserCrawlRelationEntity.bdcTaskCopyEntityList,无法初始化代理 - 没有会话未能延迟初始化角色集合:com.example.springboot.JPA.entity.UserCrawlRelationEntity .bdcTaskCopyEntityList,无法初始化代理 - 没有会话
Dao层
@Repository
public interface UserCrawlRepository extends JpaRepository<UserCrawlRelationEntity,Long>, JpaSpecificationExecutor<UserCrawlRelationEntity> {
@Repository
public interface UserCrawlRepository extends JpaRepository<UserCrawlRelationEntity,Long>, JpaSpecificationExecutor<UserCrawlRelationEntity> {
@EntityGraph(value = "UserCrawlRelationEntity.bdcTaskCopyEntityList")
List<UserCrawlRelationEntity> queryByProjectIdEquals(String projectId);
}
Entity层
@Entity
@Table(name = "user_relation")
@Data
@NamedEntityGraph(name = "UserCrawlRelationEntity.bdcTaskCopyEntityList", attributeNodes = {
@NamedAttributeNode(value = "bdcTaskCopyEntityList")
},subgraphs = {})
public class UserCrawlRelationEntity implements Serializable {
private static final long serialVersionUID = -4918127590584092681L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
/**
* 创建时间
*/
@Column(name = "create_time")
@CreatedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新时间
*/
@Column(name = "update_time")
@LastModifiedDate
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* 用户名
*/
@Column(name = "user_name")
private String userName;
@Column(name = "projectid")
private String projectId;
//@JoinTable(name = "view_dbc",
// joinColumns = {
// @JoinColumn(name = "viewProjectId", referencedColumnName = "projectId")
// },
// inverseJoinColumns = {
// @JoinColumn(name = "bdcPto", referencedColumnName = "projectId")
//})
@OneToMany(targetEntity = BdcTaskCopyEntity.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "projectId", referencedColumnName = "projectId")
Set<BdcTaskCopyEntity> bdcTaskCopyEntityList;
}
@Entity
@Table(name = "bd_copy")
@Data
@AllArgsConstructor
@NoArgsConstructor
@EntityListeners(value = AuditingEntityListener.class)
public class BdcEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "serviceid")
private String serviceId;
@Column(name = "updatetime")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@LastModifiedDate
private String updateTime;
@Column(name = "projectid")
private String projectId;
//@ManyToOne(targetEntity = UserCrawlRelationEntity.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
//@JoinColumn(name = "projectId",referencedColumnName = "projectId")
//private UserCrawlRelationEntity userCrawlRelation;
}