JPA

Java Persistence API

1. 基本注解

1.1 @Entity

@Entity 定义的对象会成为被 JPA 管理的实体,将映射到指定的数据库表中。

public @interface Entity {
    //可选,默认是此实体类名,全局唯一
    String name() default "";
}

1.2 @Table

用于指定数据库的表名。

public @interface Table {
    //表的名字,如果不写,则默认与实体的名字一样
    String name() default "";
    //此表的catalog
    String catalog() default "";
    //此表的schema
    String schema() default "";
    //唯一性约束,只有创建表的时候用,默认不需要
    UniqueConstraint[] uniqueConstraints() default {};
    //索引,只有创建表的时候使用,默认不需要
    Index[] indexes() default {};
}

1.3 @Id

定义数据库的主键,一个实体里至少有一个主键。

1.4 @IdClass

联合主键。

public @interface IdClass {
    //联合主键的类
    Class value();
}

联合主键的用处就是一个表中能存在多个主键,这些主键在关联在外部的一个对象中,这个对象需要满足以下条件

  • 必须实现 Serializable 接口
  • 必须有默认的 public 无参构造方法
  • 必须覆盖 equals()hashCode() 方法

1.5 @GeneratedValue

主键生成策略。

public @interface GeneratedValue {
    //Id生成策略,默认GenerationType.AUTO
    GenerationType strategy() default GenerationType.AUTO;
    //通过Sequences生成Id,常见的是Orcale数据库的Id生成规则,需要配合@SequenceGenerator使用
    String generator() default "";
}

public enum GenerationType {
    //通过表产生主键,框架由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植
    TABLE,
    //通过序列产生主键,通过@SequenceGenerator注解指定序列名,Mysql不支持这种方式
    SEQUENCE,
    //采用数据库Id自增,用于Mysql数据库
    IDENTITY,
    //JPA默认选项,自动选择策略
    AUTO;

    private GenerationType() {
    }
}

1.6 @Basic

@Basic 表示属性是到数据库的字段的映射,如果实体的字段上没有任何注解,默认为 @Basic

public @interface Basic {
    //EAGER是立即加载,这是默认方式,可以看到还有一种LAZY懒加载
    FetchType fetch() default FetchType.EAGER;
    //Optional 类是一个可以为null的容器对象,设置为true,则表示字段可以为null,默认可以
    boolean optional() default true;
}

public enum FetchType {
    LAZY,
    EAGER;

    private FetchType() {
    }
}

1.7 @Transient

@Transient 表示该属性并非一个到数据库表的字段的映射,是非持久化属性,与 @Basic 作用相反。

实体中有数据库中不存在的字段,可以加上 @Transient 注解,忽略这个字段的映射。

1.8 @Column

public @interface Column {
    String name() default "";
    //表示该字段是否为唯一标识,默认为false,如果表中有一个字段需要唯一标识,则既可以使用该标记,也可以使用@Table标记中的@UniqueConstraint
    boolean unique() default false;
    //数据字段是否允许为空,默认允许
    boolean nullable() default true;
    //执行insert操作时是否包含此字段,默认包含
    boolean insertable() default true;
    //执行update操作时是否包含此字段,默认包含
    //insertable和updatable属性一般多用于只读的属性,例如主键和外键等。这些字段的值通常是自动生成的
    boolean updatable() default true;
    //表示创建表时,该字段创建的SQL语句,一般用于通过Entity生成表定义时使用
    String columnDefinition() default "";
    //表示当映射多个表时,指定表的表中的字段。默认值为主表的表名
    String table() default "";
    //字段长度,默认255
    int length() default 255;
    // precision 属性和 scale 属性表示精度,当字段类型为double时,precision表示数值的总长度,scale表示小数点所占的位数
    int precision() default 0;
    int scale() default 0;
}

1.9 @Temporal

@Temporal 用来设置 Date 类型的属性映射到对应精度的字段,也就是对日期进行格式化。

public enum TemporalType {
    DATE,
    TIME,
    TIMESTAMP;

    private TemporalType() {
    }
}

public @interface Temporal {
    TemporalType value();
}

public enum TemporalType {
    DATE,
    TIME,
    TIMESTAMP;

    private TemporalType() {
    }
}

可以看到有三种格式化方式,

  1. @Temporal(TemporalType.DATE): 实体类会封装成日期 yyyy-MM-dd 的 Date 类型。
  2. @Temporal(TemporalType.TIME): 实体类会封装成时间 hh-MM-ss 的 Date 类型。
  3. @Temporal(TemporalType.TIMESTAMP): 实体类会封装成完整的时间 yyyy-MM-dd hh:MM:ss 的 Date 类型。

1.10 @Enumerated

用于直接映射枚举类型的字段。

public @interface Enumerated {
    EnumType value() default EnumType.ORDINAL;
}

1.11 @Lob

@Lob 将属性映射为数据库支持的大对象类型,支持以下两种数据库类型的字段。

  • Clob:长字符串类型,java.sql.ClobCharacter[]char[]String 都将被映射成 Clob 类型。
  • Blob:字节类型,java.sql.BlobByte[]byte[]、实现了 Serializable 接口类型都将被映射成 Blob 类型。

2. JPA 中的实体关系

简单地用原始字段持久化一个对象只是等式的一半。JPA 还具有管理彼此相关的实体的能力。表和对象中都可能存在四种实体关系:

  • 一对多
  • 多对一
  • 多对多
  • 一对一

2.1 @JoinColumn

定义外键关联的字段名称。

public @interface JoinColumn {
    //注解所在当前表的主键名,必须写
    String name() default "";
    //关联外部表的列名,默认是外部主键名
    String referencedColumnName() default "";
    //外键字段是否唯一
    boolean unique() default false;
    //外键字段是否允许为空
    boolean nullable() default true;
    //是否跟随一起新增
    boolean insertable() default true;
    //是否跟随一起更新
    boolean updatable() default true;
    
    String columnDefinition() default "";

    String table() default "";

    ForeignKey foreignKey() default @ForeignKey(ConstraintMode.PROVIDER_DEFAULT);
}

配合 @OneToOne@OneToMany@ManyToOne 一起使用。

2.2 关系映射注解

public @interface OneToOne {
    //关系目标实体,默认为该字段的类型
    Class targetEntity() default void.class;
    
    //级联操作策略
    CascadeType[] cascade() default {};

    //数据获取方式,立即加载和延迟加载
    FetchType fetch() default FetchType.EAGER;

    boolean optional() default true;

    //关联关系被谁维护,一般不需要特别指定, 只有关系维护方才能操作两者的关系,被维护方即使设置了维护方属性进行存储也不会更新外键关联
    //mappedBy不能与@JoinColumn或者@JoinTable同时使用
    //mappedBy的值是指另一方的实体里属性的字段,而不是数据库字段,也不是实体的对象的名字,是另一方配置了@JoinColumn或者@JoinTable注解的属性的字段名称
    String mappedBy() default "";

    //是否级联删除,和 CascadeType.REMOVE 的效果一样,只是配置了两种中的一种就会自动级联删除
    boolean orphanRemoval() default false;
}

public enum CascadeType { 
    ALL, 
    PERSIST, 
    MERGE, 
    REMOVE,
    REFRESH
}

级联操作策略代码举例解释

public class Student {
    @ManyToMany(cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
    private Set<Course> courses = new HashSet<>();
}
  1. CascadeType.PERSIST 级联新建:若 Student 实体持有的 Course 实体在数据库中不存在时,Student 保存时自动在 Course 实体对应的数据库中保存 Course 数据。
  2. CascadeType.MERGE 级联更新:当 Student 中的数据改变,会相应地更新 Course 中的数据。
  3. CascadeType.REMOVE 级联删除:删除 Student 实体,与它有映射关系的 Course 实体也会跟着被删除。
  4. CascadeType.REFRESH 级联刷新:Student 保存时重新加载 Course 关系
  5. CascadeType.DETACH 级联脱离:要删除一个实体,直接撤销所有相关的外键关联
  6. CascadeType.ALL 拥有以上所有级联操作权限
  7. 默认,关系表不会产生任何影响

@OneToOne 需要配合 @JoinColumn 一起使用

在使用@OneToOne进行双向关联时,需要在类上加上注解 @JsonIdentityInfo,这个注解被用来在序列化/反序列化时为该对象或字段添加一个对象识别码,通常是用来解决循环嵌套的问题。通过配置属性 generator 来确定识别码生成的方式,配置属性 property 来确定识别码的名称,识别码名称没有限制。

一般这个注解可以这么加

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")

@OneToMany@ManyToOne 同上。

2.3 @OrderBy

用于关联查询时排序。

@Entity 
public class Course {
    ...
    @ManyToMany
    @OrderBy("lastname ASC")
    public List  getStudents() {...};
    ...
}

@Entity 
public class Student {
    ...
    @ManyToMany(mappedBy="students")
    @OrderBy //默认使用主键排序
    public List  getCourses() {...};
    ...
}

3. 查询

注意: 这里是接口,是 persistence-api 包中的,项目里 Repository 里的接口上面实现的 @Query 注解不是下面这个,spring-data-jpa 的查询和解析留待下回分解

/**
 * Interface used to control query execution.
 *
 * @since Java Persistence 1.0
 */
public interface Query {

    /**
     * Execute a SELECT query and return the query results
     * as a List.
     * @return a list of the results
     * @throws IllegalStateException if called for a Java 
     *    Persistence query language UPDATE or DELETE statement
     */   
    public List getResultList();

    /**
     * Execute a SELECT query that returns a single result.
     * @return the result
     * @throws NoResultException if there is no result
     * @throws NonUniqueResultException if more than one result
     * @throws IllegalStateException if called for a Java 
     *    Persistence query language UPDATE or DELETE statement
     */
    public Object getSingleResult();

    /**
     * Execute an update or delete statement.
     * @return the number of entities updated or deleted
     * @throws IllegalStateException if called for a Java 
     *    Persistence query language SELECT statement
     * @throws TransactionRequiredException if there is
     *    no transaction
     */
    public int executeUpdate();

    /**
     * Set the maximum number of results to retrieve.
     * @param maxResult
     * @return the same query instance
     * @throws IllegalArgumentException if argument is negative
     */
    public Query setMaxResults(int maxResult);

    /**
     * Set the position of the first result to retrieve.
     * @param startPosition the start position of the first result, numbered from 0
     * @return the same query instance
     * @throws IllegalArgumentException if argument is negative
     */
    public Query setFirstResult(int startPosition);

    /**
     * Set an implementation-specific hint.
     * If the hint name is not recognized, it is silently ignored.
     * @param hintName
     * @param value
     * @return the same query instance
     * @throws IllegalArgumentException if the second argument is not
     *    valid for the implementation
     */
    public Query setHint(String hintName, Object value);

    /**
     * Bind an argument to a named parameter.
     * @param name the parameter name
     * @param value
     * @return the same query instance
     * @throws IllegalArgumentException if parameter name does not
     *    correspond to parameter in query string
     *    or argument is of incorrect type
     */
    public Query setParameter(String name, Object value);

    /**
     * Bind an instance of java.util.Date to a named parameter.
     * @param name
     * @param value
     * @param temporalType
     * @return the same query instance
     * @throws IllegalArgumentException if parameter name does not
     *    correspond to parameter in query string
     */
    public Query setParameter(String name, Date value, TemporalType temporalType);

    /**
     * Bind an instance of java.util.Calendar to a named parameter.
     * @param name
     * @param value
     * @param temporalType
     * @return the same query instance
     * @throws IllegalArgumentException if parameter name does not
     *    correspond to parameter in query string
     */
    public Query setParameter(String name, Calendar value, TemporalType temporalType);

    /**
     * Bind an argument to a positional parameter.
     * @param position
     * @param value
     * @return the same query instance
     * @throws IllegalArgumentException if position does not
     *    correspond to positional parameter of query
     *    or argument is of incorrect type
     */
    public Query setParameter(int position, Object value);

    /**
     * Bind an instance of java.util.Date to a positional parameter.
     * @param position
     * @param value
     * @param temporalType
     * @return the same query instance
     * @throws IllegalArgumentException if position does not
     *    correspond to positional parameter of query
     */
    public Query setParameter(int position, Date value, TemporalType temporalType);

    /**
     * Bind an instance of java.util.Calendar to a positional parameter.
     * @param position
     * @param value
     * @param temporalType
     * @return the same query instance
     * @throws IllegalArgumentException if position does not
     *    correspond to positional parameter of query
     */
    public Query setParameter(int position, Calendar value, TemporalType temporalType);

    /**
     * Set the flush mode type to be used for the query execution.
     * The flush mode type applies to the query regardless of the
     * flush mode type in use for the entity manager.
     * @param flushMode
     */
    public Query setFlushMode(FlushModeType flushMode);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容