JPA提供了一种简单高效的方式来管理Java对象(POJO)到关系数据库的映射,此类Java对象称为JPA实体或简称实体。实体一般与底层数据库中的单个关系表相关联,每一个实体的实例表示数据库表格中的某一行。java
实体管理器(EntityManager)用于管理系统中的实体,它是实体与数据库之间的桥梁,经过调用实体管理器的相关方法能够把实体持久化到数据库中,同时也能够把数据库中的记录打包成实体对象。数据库
在此以前咱们要先了解实体的状态及其转换,见下图数组
JPA实体生命周期有四种状态app
状态名 | 做为java对象存在 | 在实体管理器中存在 | 在数据库存在 |
New | Y | N | N |
Managed | Y | Y | Y |
Datached | N | N | N |
Removed | Y | Y | N |
用一段程序来示范ide
@Transactional public void save(){ //New 状态 Task t = new Task(); t.setTaskName("task" + new Date().getTime()); t.setCreateTime(new Date()); //Managed状态 em.persist(t); //实体类t已经有id t.getId(); t.setTaskName("kkk"); //更新任务名称,这时,若是提交事务,则直接将kkk更新到数据库 //Detached状态 事务提交或者调用em.clear都直接将实体任务状态变为Detached em.clear(); t.setTaskName("kkk"); //更新数据不会更新到数据库 //Removed状态 em.remove(t); }
对应于实体的四种状态,实体管理器有四种经常使用的方法,分别是:persist / merge / clear / remove,结合状态图,能够判断,对于不一样状态下的实体,各个方法操做结果会有不一样:fetch
对于不一样状态下的实体,persist 操做结果以下:ui
对于不一样状态下的实体,merge 操做结果以下:spa
对于不一样状态下的实体,refresh 操做结果以下:hibernate
对于不一样状态下的实体,remove 操做结果以下:code
public class UserRepositoryImpl { @PersistenceContext private EntityManager entityManager; @Transactional public void add(User user) { entityManager.persist(user); } @Transactional public User update(User user) { User userUpdate = entityManager.find(User.class, user.getId()); userUpdate.setAddress(user.getAddress()); userUpdate.setName(user.getName()); userUpdate.setPhone(user.getPhone()); return userUpdate; } @Transactional public User addOrUpdate(User user) { return entityManager.merge(user); } @Transactional public void delete(User user) { entityManager.remove(user); } public User findOne(Integer id) { return entityManager.find(User.class, id); } public List<User> findAll() { String queryString = "select u from User u"; Query query = entityManager.createQuery(queryString); return query.getResultList(); } }
@Entity //表示该类为JPA实体类 @Table(name="t_user") //对应数据库中哪张表 public class User { @Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //对应数据库表中哪一个列字段及对该字段的自定义 private String phone;
@Id //标明主键 @GeneratedValue //主键生成策略 @Column(name="id_") private Integer id;
更多的主键生成策略,详见3.6 的整体代码
@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //对应数据库中哪一个列及对该字段的自定义 private String phone;
一般一个实体对应于一个表格,即表格中的全部的实体属性都存放于一张表,若是将实体的属性分配到多个表格存放,就涉及到单实体多表格存储
@Entity @Table(name="t_user",catalog="",schema="") @SecondaryTables({ //指明存放的第二张表 @SecondaryTable(name = "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id")) }) public class User { @Column(name="name_", length=60, nullable=false,unique=true,insertable=false) private String name; //分表存储 @Column(table = "t_address", name="street_", length = 100) private String street;
在定义实体时可能须要将某几个的属性剥离出放到另一个实体中,以使程序更有层次感,而且当其余实体也须要这几个属性时,咱们也不须要再定义这几个属性,把存放这几个属性的实体从新引用便可,操做方法以下:
@Embeddable //标识该实体可嵌入到其余实体中 public class Comment { @Column(name="title_",length=100) String title; @Column(name="content_") String content;
/* //被剥离出的属性 @Column(name="title_",length=100) String title; @Column(name="content_") String content; */ @Embedded //引入该实体 @AttributeOverrides({ //罗列出全部须要从新命名的属性 @AttributeOverride(name = "title", column = @Column(name = "user_title")), @AttributeOverride(name = "content", column = @Column(name = "user_content")) }) private Comment comment;
内嵌实体在数据库中不会一点单独的表格存放,而是跟数组实体存放于同一表格中。
import java.math.BigDecimal; import java.util.Date; import javax.persistence.*; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name="t_user",catalog="",schema="") @SecondaryTables({ @SecondaryTable(name = "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id")) }) public class User { @Id //标明主键 @GeneratedValue //主键生成策略 @Column(name="id_") private Integer id; /* @Id @GeneratedValue(generator="uuidGenerator") @GenericGenerator(name="uuidGenerator",strategy="uuid") @Column(name="id_",length=32) private String id;*/ /* @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name="id_") private Integer id;*/ /* * @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="id_") private Integer id;*/ /* @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idGenerator") @SequenceGenerator(name = "idGenerator",sequenceName="mySeq",allocationSize=1) @Column(name="id_") private Integer id;*/ /* @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "userGenerator") @TableGenerator(name = "userGenerator",table="pk_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="user_pk", initialValue=0, allocationSize=1) @Column(name="id_") private Integer id; */ @Column(name="name_", length=60, nullable=false,unique=true,insertable=false) private String name; @Column(name="address_", length=60, nullable=false) private String address; @Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") private String phone; @Column(name="inCome_", precision=12, scale=2) private BigDecimal inCome; @Temporal(TemporalType.DATE) private Date birthday; //Date 日期型,精确到年月日,例如“2008-08-08” //Time 时间型,精确到时分秒,例如“20:00:00” //Timestamp 时间戳,精确到纳秒,例如“2008-08-08 20:00:00.000000001” @Lob @Column(name="pic_") @Basic(fetch=FetchType.LAZY) private byte[] pic; @Lob @Column(name="note_") @Basic(fetch=FetchType.LAZY) private String note; //分表存储 @Column(table = "t_address", name="street_", length = 100) private String street; //分表存储 @Column(table = "t_address", name="city_") private String city; //分表存储 @Column(table = "t_address", name="conutry_",length = 20) private String conutry; @Column(name="title_",length=100) String title; @Column(name="content_") String content; /* @Embedded //引入该实体 @AttributeOverrides({ //罗列出全部须要从新命名的属性 @AttributeOverride(name = "title", column = @Column(name = "user_title")), @AttributeOverride(name = "content", column = @Column(name = "user_content")) }) private Comment comment; */ //省略get/set方法 }
以下例,人员表(person)和地址表(adddress),person表是关系的拥有者,表中的address_id字段关联着address表的主键id。
@Entity public class Person { //略
@OneToOne @JoinColumn(name="address_id",referencedColumnName="aid")//name:主表的外键字段; referencedColumnName:从表的主键 //若是关联的字段有多个,采用以下注解 //@JoinColumns(value={@JoinColumn(name="address_id",referencedColumnName="aid"),@JoinColumn(name="address_id2",referencedColumnName="aid2")}) private Address address;
部门表(depart)和员工表(employee),一个部门能够有多个员工,一对多关系能够采用以下两种实现方法。
建立中间表(depart_employee),表中存放两个表的主键。经过部门id可查询关联员工的id,三张表存在两个主外键关系。
@Entity public class Depart { //略 @OneToMany @JoinTable(name = "depart_employee", //name:关联表 joinColumns = @JoinColumn(name = "depart_id",referencedColumnName="did"), //joinColumns:关系的拥有者与关联表的关系 inverseJoinColumns = @JoinColumn(name = "employee_id",referencedColumnName="eid"))//inverseJoinColumns:关系的被拥有者与关联表的关系 private List<Employee> employees;
在员工表(employee2)中添加一个depart_id字段,它做为外键关联部门表(depart2)的主键id。
@Entity public class Depart2 { //略 @OneToMany @JoinColumn(name="depart_id",referencedColumnName="id") private List<Employee2> employee2s;
多对多的实现也是经过中间表,方法同一对多的中间表实现方式。
@Entity public class Teacher { //略 @ManyToMany @JoinTable(name = "teacher_student", joinColumns = @JoinColumn(name = "teacher_id",referencedColumnName="tid"), inverseJoinColumns = @JoinColumn(name = "student_id",referencedColumnName="sid")) private List<Student> students;
@Entity public class Student { //略 @ManyToMany(mappedBy = "students") private List<Teacher> teachers;
以@OneToOne为例,当我但愿删除人员信息时,也将其地址信息删除,则可以使用级联策略;当我想要查询人员信息(主实体)时,并不想同时查询出其地址信息(子实体),能够设置懒加载。
@Entity public class Person { @OneToOne(cascade={CascadeType.REFRESH,CascadeType.REMOVE},fetch=FetchType.LAZY) //@JoinColumn(name="address_id",referencedColumnName="aid") private Address address;
继承[extends]想必已不陌生,对于JPA来讲,咱们不但要考虑如何实现Java端的继承关系,还要考虑如何持久化到数据库中。JPA为此提供了三种策略,以下:
继承关系如图,继承策略的注解主要应用于父类Item。
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public class Item {
执行单一表策略会将全部实体的信息存放于一张表中,它的优势是信息存放于一张表,查询效率较高,缺点是大量字段为空,浪费存储空间。
若是类名过长或须要更改鉴别字段的名称,可对鉴别字段及可选值自定义:
@Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="ITYPE",discriminatorType=discriminatorType.CHAR) //声明鉴别字段的字段名,类型 @DiscriminatorValue("I") //该表在鉴别字段列显示的值 public class Item {
@Entity @DiscriminatorValue("P") public class Phone extends Item {
@Entity @DiscriminatorValue("B") public class Book extends Item {
效果以下
@Entity @Inheritance(strategy = InheritanceType.JOINED) public class Item {
链接表策略会生成三张表,经过共享主键彼此关联。
这种策略避免了空字段的浪费,但因为采用表关联查询,当数据量过大时,查询效率较低。
@Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) public class Item { /* @Id @GeneratedValue(strategy = GenerationType.AUTO) */ @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "ItemGenerator") @TableGenerator(name = "ItemGenerator",table="pk_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="item_pk", initialValue=0, allocationSize=1) private Long id;
每一个类策略其实是每一个类一个表策略,这种策略要求主键不能使用自增的方式,如上面的代码,采用表中获取的方式。
三张表各自存放本身的完整信息,表之间没有任何的关联关系。虽然他们各自存放各自的数据,但主键是连续的。即三个表共用一套主键生成策略(三个表的主键都从另外一个表中获取)。
这种策略查询效率高,同时也不存在大量空字段的浪费。