JPA 中可以使用一套比较简洁的注解来配置实体类信息,并且通过给出的大量默认值让这一配置显得格外的方便;
JPA的实体注解的访问方式主要分为三种:
字段访问:把注解都写在对象的字段上面,字段必须不是public的
属性访问:把注解写在getter方法上面,属性也必须有setter方法,方法必须是public和protected的。
混合访问:即指在此类上同时使用了字段访问和属性访问。
上面说的配置访问的方式主要体现在@Access 注解上。这个注解可以放到类上面就可以表示整个类的访问方式,也可以放到某个字段或getter方法上,达到某个字段用某种方式访问的效果,如果没有配置默认使用字段访问模式。
由于注解太多,这里在一个实体类中展示常用的一些注解,演示使用的是字段访问模式;
//标识此类为JPA实体类
@Entity
public class UserProfile implements Serializable {
/**
* @Id 用于标识当前字段是表的主键
*
* 有了主键,那么肯定就需要定义主键生成策略了。常用的策略一个是自增长,一个是序列。
*
* 小编这里用的是 通过一张表,来管理ID的生成,该策略的主要思路是;
* 在表中定义一个key列 来标识该行记录存属于某实体的id值,value列 则为一个 数字;如:
*
**************************************************
*
* key列 | 值列 |
*
**************************************************
*
* UserProfile | 1 |
*
***************************************************
*
* 这个value 并不代表id。它还有个乘数比如100.那么1就表示(1*100)个数。
*
* 这100个数就被放到内存里面了,等这100个数用完后,
*
* 就去数据库里面拿发现数据库里面是1.那么就是说要把+1的值拿到内存里面
*
* 也就是 100 - 200 了。这样数据中的值就变成2了。
*
* 我们在来@TableGenerator里面定义的数据就好理解了,这种ID创建表的表面,key列,value列,
*
*当前表对应的key值。 乘数的值,产生的id的初始化值
*
* 这个创建器定义好后,我们还需要做的一步就是告诉JPA我们用的是哪个策略
* @GeneratedValue strategy选择用表作为ID生成策略,然后在关联上 上面配置好的创建器。
**/
@Id
@TableGenerator(name = "UserProfile", table = "ID_SEQUENCE",
pkColumnName = "ID_KEY", valueColumnName = "VALUE",
pkColumnValue = "UserProfile", allocationSize = 100, initialValue = 0)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "UserProfile")
private int id;
/**
*
* @Column 来设置该字段对应数据库表的一些信息.
* 如:是否唯一,能不能为空,长度多少,对应的列名(缺省等于字段名)
*/
@Column(unique = true, nullable = false, length = 45)
private String username;
/**
* @Log 用来标识大数据字段
*/
@Log
private String content;
/**
* @Basic 该注解标识 当前字段是一个基础数据库字段;
* 包括(八大基本类型,Date,String,BigDecimal,BigInteger,byte[],char[])
* fetch 属性用来标识 这个字段 使不使用 懒加载,这里配置的是使用。@Basic中缺省是饥渴加载。
*/
@Basic(fetch = FetchType.LAZY)
@Column(nullable = false, length = 45)
private String password;
/**
* @OneToOne 用于关联另一张表且是一对一的情况。如果配置mappedBy那么说明当前的是主表,
* 关联的那个表示从表,当前表的id被作为外键在关联表上了
*/
@OneToOne(mappedBy = "userProfile")
private UserDetail detail;
/**
* @Enumerated 这个注解作用在 枚举上面,表示枚举存储到数据库的形式。
* 有两种一种是存枚举的名字,一种是存序号。
* 小编这里推荐存名字,浅显易懂。
* 存序号的号,要额外注意,那就是加入新的值是,只能往已有的值后面加。
* 否则实际值和数据库中存的序号就对应不上了
*/
@Enumerated(EnumType.STRING)
private UserStatus status;
/**
* @ManyToOne用于关联另一张表且是多对一的情况,当前实体是多方故需要只有关联方主键作为外键。
* 多对一的 fetch缺省是饥渴模式,所以根据自身的业务可以进行调整
* @JoinColumn 对持有的外键列,进行配置,如:列名,是否允许控制。。。
*/
@ManyToOne(optional = true)
@JoinColumn(name = "locationId", nullable = true)
private Location location;
/**
* @OneToMany 用于关联另一张表且是一对多的情况,当前实体是一方所以不想要持有外键,
* 只需要配置一下和关联实体中的哪个字段关联即可,fetch缺省是懒加载
*/
@OneToMany(mappedBy = "userProfile")
private List userProfileHistory;
/**
* @Temporal 用来确定时间类型的。值得对应如下
* TIMESTAMP ---》 java.sql.Timestamp
* DATE ----》java.sql.Date
* TIME ----》 java.sql.Time
* 一般使用时间戳。 用不到具体分秒时的就选用DATE 了。
*/
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
/**
* @ManyToMany 用于关联另一张表且是多对多的情况,fetch缺省是懒加载。
* 构建多对多,需要定义一下中间表的配置
* @JoinTable 就是用来定义中间表的, name表名称。
* inverseJoinColumns 定义关联实体的主键 列的信息,
* joinColumns 定义当前实体的主键 列信息。定义好后大致如下
**************************************************
* role_id | user_id |
**************************************************
* 1 | 1 |
**************************************************
* OK 为了方便多对多的管理,多数情况下 会有 级联删除,我被删除啦关联的你也应该被删除。
* 上面这种业务 可以在 @ManyToMany cascade属性上进行配置。
*/
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "role_user",
inverseJoinColumns = { @JoinColumn(name = "role_id") },
joinColumns = { @JoinColumn(name = "user_id") })
private List roles;
/**
*
* @Version 应用在需要进行一些字段维护的实体上。该字段是用来做 乐观锁的。
*
* 那么说到了乐观锁:这里就简单的描述一下,举一个例子吧。
*
* 小明同学要请假陪女票过七夕,在OA 上提交了请假3天的请假条。
* 经理看到了,觉得太久了给他扣掉了一天。人力看到了也觉得请3天太久了,也给他扣了一天。
* 那么我们期望的值就是小明的请假条批的是一天。然而经理和人力都是在请假条是3天的那个状态看到的,
* 然后扣一天也是在3的基础上。那么真实情况就变成了2天。
* 小明当然就开心了,但我们还是要杜绝这种事情的发生,毕竟不是真实想要的结果.
* 来看乐观锁是怎么解决这一问题的。我们先为每条记录都加一个版本字段,
* 那么在某条记录添加时版本的初始值为0,
* 一条记录被修改了它的版本就会+1.
* 我们回到上面那个例子,经理进行修改的请假条版本为0(没有被改过).
* 首先这条记录和数据库中的对应记录的版本做对比,OK 都是0,可以修改,那么经理这条记录就修改成功,版本+1.
* 人力修改的记录也过来了,人力修改的这条记录的版本也是0(查出来看的时候也没被改过),
* 那么和数据库中的版本进行对比,一比发现版本对不上,不能修改,就会抛出一个叫乐观锁的异常,打断这次修改.
* 接收到乐观锁异常后,可以提示用户再操作,或者系统再回调一次。
*
*/
@Version
private int version;
}