JPA与Hibernate的关系
什么是JPA
JavaEE5.0的平台的ORM标准规范。为应用程序访问持久层提供统一的方式。
JPA是规范,Hibernate是实现。从功能上来讲,JPA是Hibernate的一个子集。Hibernate只是JPA的一个提供商。类似于JDBC和JDBC驱动提供商。
JPA的优点
JPA提供了更加简洁的,基于注解的方式来进行映射。
JPA配置文件
要求
必须配置在META-INF目录下,且配置名字是固定的persistence.xml。
顶级节点为,次级节点为,JPA的绝大部分信息配置在节点内。
节点name属性:用来指定这个persistence单元的名称
节点transaction-type属性:指定JPA的事务处理策略,默认为RESOURCE_LOCAL,数据库级别的事务,只能针对一种数据库,不支持分布式事务。若需要支持分布式事务,使用JTA,即:transaction-type="JTA"。
如果JPA的提供者存在多个的时候,需要在节点配置具体使用哪个提供者。需要是javax.persistence.spi.PersistenceProvider的实现类。
在节点内通过节点配置数据库连接信息。
在节点内通过节点配置属于具体实现的特性信息。
通过节点来配置映射的实体类。
举例
org.hibernate.ejb.HibernatePersistence
com.solverpeng.jpa.Customer
com.solverpeng.jpa.Orders
com.solverpeng.jpa.Clazz
com.solverpeng.jpa.Student
注意事项
指定JPA实现后,在配置数据库驱动、连接、用户名和密码的时候,需要指定前缀。
通过IntellijIdea默认生成的实体类,id没有指定生成方式,需要通过@GeneratedValue来指定。
实体注解
基本注解
@Entity:顾名思义,用来映射实体
@Table:当实体类和数据库表名不一致时,需要通过@Table的name属性来指定表名,需要和@Entity注解同时使用。
@Id:用来映射主键列。
@Column:当属性名和列表不一致时,可以通过@Column的name属性来指定映射的列名。
@GeneratedValue:用来映射主键生成策略。
IDENTITY:ID自增的方式
AUTO:自动选择一个底层数据库最适合的主键生成策略。
SEQUENCE:通过序列产生主键。
TABLE:通过表产生主键。参见:@TableGenerator注解。
@Basic:映射一个简单的属性到数据库,可以省略不写。
特殊注解
@Transient:表示标注的属性不进行映射。
@Temporal:映射日期时,指定日期精度。
@TableGenerator:通过表来产生主键,如:
CREATETABLEid_generators
(
idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
pk_nameVARCHAR(50),
id_valueINT(10)
);
@TableGenerator(name="id_generator",table="id_generators",
pkColumnName="pk_name",pkColumnValue="customer_id",
valueColumnName="id_value",allocationSize=10)
@GeneratedValue(strategy=GenerationType.TABLE,generator="id_generator")
API
Persistence接口
主要作用是通过一个静态方法获取EntityManagerFactory接口。
EntityManagerFactory接口
主要作用是获取EntityManager,类似于Hibernate中的SessionFactory接口。
EntityManager接口
find():类似于HibernateSession接口的get()方法。
getReference():类似于HibernateSession接口的load()方法。采用懒加载的策略。
persist():类似于HibernateSession接口的persist()方法,对一个游离对象执行persist()方法时,会抛出异常。
remove():和HibernateSession接口的delete()方法类似,但是只能删除持久化对象,而不能删除游离对象。
四种状态:参见HIbernateSession。
flush():同步一级缓存中数据到数据库。
refresh():同步数据库的记录到一级缓存中。
merge():
若保存的是一个临时对象,则会创建一个新的对象,将临时对象中的属性值复制到新创建的对象,然后对这个新的对象执行保存操作,执行meger()方法后返回新的对象的引用。
若保存的是一个游离对象,该对象在缓存中不存在对应的记录,在数据库中也不存在对应的记录,则会当成一个临时对象去处理。
若保存的是一个游离对象,该对象在缓存不存在对应的记录,在数据库中存在对应的记录,则会从数据库中加载该OID对应的对象,将游离对象的属性值赋值给从数据库中加载的对象,执行UPDATE操作。
若保存的是一个游离对象,该对象在缓存中存在对应的记录,且在数据库中存在对应的记录,则会把游离对象中的属性值赋值到缓存对象中,再执行UPDATE操作。
关联关系
单向多对一
以Customer和Orders为例,Orders端存有Customer端的外键。
建表语句:
CREATETABLEorders
(
order_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
order_nameVARCHAR(50),
customer_id_fkINT(11),
CONSTRAINTFK_vgghm8989rfgolmxd1mfw3x8FOREIGNKEY(customer_id_fk)REFERENCEScustomer(customer_id)
);
CREATEINDEXFK_vgghm8989rfgolmxd1mfw3x8ONorders(customer_id_fk);
CREATETABLEcustomer
(
customer_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
customer_nameVARCHAR(50),
birthdayDATE
);
生成的实体:
@Entity
publicclassOrders{
privateIntegerorderId;
privateStringorderName;
privateCustomercustomer;
@Id
@GeneratedValue
@Column(name="order_id")
publicIntegergetOrderId(){
returnorderId;
}
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="customer_id_fk",referencedColumnName="customer_id")
publicCustomergetCustomer(){
returncustomer;
}
}
说明:
通过@JoinColumn注解来指定外键映射,通过其name属性指定映射的外键列名,通过referencedColumnName来指定映射的关联的1的一端的哪列。
在保存的时候,和Hibernate一样,先保存1的一端会有助于性能的提升。
在获取关联的1的一端时,默认是立即加载,可以通过修改@ManyToOne的fetch属性为FetchType.LAZY来改为懒加载策略。
在删除1的一端时,若存在关联的记录,则抛出异常。
单向一对多
以Clazz和Student为例。Clazz中拥有Set集合,Student中拥有Clazz的外键。
建表语句:
CREATETABLEstudent
(
student_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
student_nameVARCHAR(50),
class_id_fkINT(11),
CONSTRAINTFK_38k5ipq4p5s8k0a2bhqcbvsl8FOREIGNKEY(class_id_fk)REFERENCESclass(class_id)
);
CREATEINDEXFK_38k5ipq4p5s8k0a2bhqcbvsl8ONstudent(class_id_fk);
CREATETABLEclass
(
class_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
class_nameVARCHAR(50)
);
生成的实体:
@Entity
@Table(name="class",schema="jpa")
publicclassClazz{
privateIntegerclassId;
privateStringclassName;
privateSetstudents=newHashSet<>();
@Id
@GeneratedValue
@Column(name="class_id")
publicIntegergetClassId(){
returnclassId;
}
@JoinColumn(name="class_id_fk")
@OneToMany(fetch=FetchType.EAGER)
publicSetgetStudents(){
returnstudents;
}
}
说明:
执行保存的时候,不论是先保存1的一端还是先保存n的一端,都需要发送额外的UPDATE语句用来维护关联关系。
执行获取的时候,通过1的一端获取n的一端,默认采用的是懒加载策略,可以通过修改@OneToMany的fetch属性为FetchType.EAGER来立即加载。
执行删除的时候,若删除的是1的一端的对象,默认情况下,会将关联的n的一端的相应的记录外键列值为null。可以通过修改@OneToMany的cascade属性来达到想要的效果。
双向多对一
以Family和People为例,Family中拥有Set的集合,People中有Customer类型的成员变量。People表中有Family的外键。
建表语句:
CREATETABLEfamily
(
family_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
family_nameVARCHAR(50)
);
CREATETABLEpeople
(
people_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
people_nameVARCHAR(50),
family_id_fkINT(11)
);
生成的实体:
@Entity
publicclassPeople{
privateIntegerpeopleId;
privateStringpeopleName;
privateFamilyfamily;
@Id
@GeneratedValue
@Column(name="people_id")
publicIntegergetPeopleId(){
returnpeopleId;
}
@ManyToOne
@JoinColumn(name="family_id_fk",referencedColumnName="family_id")
publicFamilygetFamily(){
returnfamily;
}
}
@Entity
publicclassFamily{
privateIntegerfamilyId;
privateStringfamilyName;
privateSetpeople=newHashSet<>();
@Id
@GeneratedValue
@Column(name="family_id")
publicIntegergetFamilyId(){
returnfamilyId;
}
@OneToMany(mappedBy="family")
publicSetgetPeople(){
returnpeople;
}
}
说明:
若在1的一端@OneToMany中使用了mappedBy属性,则@OneToMany属性就不能再添加@JoinColumn的属性了。该属性用来指定这端为被维护端。
保存:若先保存n的一端,再保存1的一端,会多发送UPDATE语句,因为关联关系是在n的一端维护的。
查询:从1的一端获取n的一端的集合时,采用的延迟检索的策略。从n的一端获取1的一端的对象时,是采用的立即检索的策略。
删除:删除1的一端时,因为外键约束,所以会抛出异常。
双向一对一
以Department和Manager为例。一个部门存在一个经理,一个经理只能属于一个部门。Manger端维护Department的外键。
建表语句:
CREATETABLEdepartment
(
dept_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
dept_nameVARCHAR(50)
);
CREATETABLEmanager
(
manager_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
manager_nameVARCHAR(50),
dept_id_fkINT(11)
);
生成实体:
@Entity
publicclassDepartment{
privateIntegerdeptId;
privateStringdeptName;
privateManagermanager;
@Id
@GeneratedValue
@Column(name="dept_id")
publicIntegergetDeptId(){
returndeptId;
}
@OneToOne(mappedBy="dept")
publicManagergetManager(){
returnmanager;
}
}
@Entity
publicclassManager{
privateIntegermanagerId;
privateStringmanagerName;
privateDepartmentdept;
@Id
@GeneratedValue
@Column(name="manager_id")
publicIntegergetManagerId(){
returnmanagerId;
}
@OneToOne(fetch=FetchType.LAZY)
@JoinColumn(name="dept_id_fk",referencedColumnName="dept_id")
publicDepartmentgetDept(){
returndept;
}
}
说明:
没有外键列的一端,使用@OneToOne的mappedBy属性,用来指定自己为被维护的一端。
保存:建议先保存不维护关联关系的一端,这样不会多出UPDATE语句。
获取:默认情况下,若获取维护关联关系的一方,则会通过立即检索的策略获取其关联的对象。可以通过@OneToOne的fetch属性来修改加载策略。若获取不维护关联关系的一方,则也会通过左外链接的方式获取其关联的对象,纵然修改了@OneToOne的fetch属性,也不起作用,因为它并不知道谁和它关联着。
删除:因为存在在外键约束,所以删除不维护关联关系的一端时,若存在着和其关联的记录。则抛出异常。
双向多对多
以Categroy和Item为例。创建了一张中间表:categories_items。
建表语句:
CREATETABLEcategory
(
cate_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
cate_nameVARCHAR(50)
);
CREATETABLEitem
(
item_idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
item_nameVARCHAR(50)
);
CREATETABLEcategories_items
(
idINT(11)PRIMARYKEYNOTNULLAUTO_INCREMENT,
cate_id_fkINT(11),
item_id_fkINT(11)
);
生成的实体:
@Entity
publicclassCategory{
privateIntegercateId;
privateStringcateName;
privateSet- items=newHashSet<>();
@Id
@GeneratedValue
@Column(name="cate_id")
publicIntegergetCateId(){
returncateId;
}
@ManyToMany(mappedBy="categories")
publicSet- getItems(){
returnitems;
}
}
@Entity
publicclassItem{
privateIntegeritemId;
privateStringitemName;
privateSetcategories=newHashSet<>();
@Id
@GeneratedValue
@Column(name="item_id")
publicIntegergetItemId(){
returnitemId;
}
@ManyToMany
@JoinTable(name="categories_items",schema="jpa",
joinColumns=@JoinColumn(name="item_id_fk",referencedColumnName="item_id"),
inverseJoinColumns=@JoinColumn(name="cate_id_fk",referencedColumnName="cate_id"))
publicSetgetCategories(){
returncategories;
}
}
说明:
Category端放弃维护关联关系。使用@ManyToMany的mappedBy属性。
使用@JoinTable来指定中间表。
name属性:指定中间表名
joinColumns属性:指定本类在中间表的外键列名(name)和本类中与中间表外键对应的主键(referencedColumnName)
inverseJoinColumns属性:指定关联的另一端的类在中间表的外键列名(name)和外键所对应的主键(referencedColumnName)
获取:不论是通过维护关联关系的一端,还是不维护关联关系的一端来获取集合对象,采用的都是懒加载策略。
删除:删除不维护关联关系的一端时,若存在与其关联的记录,则抛出异常。而删除维护关联关系的一端则没有问题。
二级缓存
配置举例
org.hibernate.ejb.HibernatePersistence
com.solverpeng.jpa.Customer
ENABLE_SELECTIVE
value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
配置步骤
导入缓存相关Jar包。
在persistence.xml文件中配置缓存相关属性以及节点。
在需要缓存的实体类添加@Cacheable注解。
节点
若JPA实现支持二级缓存,该节点可以配置在当前持久化单元中是否启用二级缓存,可以配置如下值:
ALL:所有的实体类都被缓存
NONE:所有的实体类都不被缓存
ENABLE_SELECTIVE:标识@Cacheable(true)注解的实体类将被缓存
DISABLE_SELECTIVE:标识@Cacheable(false)注解的实体类将不被缓存
需要注意在该节点在配置文件中的位置
JPQL
基本查询
占位符查询
Triberesult=(Tribe)manager.createQuery("fromTribetwheret.tribeId=?").setParameter(1,1).getSingleResult();
投影查询
List |
|