目录
2.2.3 第三步:编写工具类,用于获取JPA操作数据库对象
2.4.1 IDENTITY:主键由数据库自动生成(自动增长型)
2.4.2 SEQUENCE:根据底层数据库序列来生成主键,条件是数据库支持序列
第一章:JPA概述
1.1 JPA概述
JPA是什么,首先要知道EJB。
EJB是sun的JavaEE服务器端组件模型,设计目标与核心应用是部署分布式应用程序。简单来说就是把已经编写好的程序(即:类)打包放在服务器上执行。凭借java跨平台的优势,用EJB技术部署的分布式系统可以不限于特定的平台。EJB (Enterprise JavaBean)是J2EE(javaEE)的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准。其特点包括网络服务支持和核心开发工具(SDK)。 在J2EE里,Enterprise Java Beans(EJB)称为Java 企业Bean,是Java的核心代码,分别是会话Bean(Session Bean),实体Bean(Entity Bean)和消息驱动Bean(MessageDriven Bean)。在EJB3.0推出以后,实体Bean被单独分了出来,形成了新的规范JPA。
JPA(Java Persistence API),是java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
简单来说,EJB是Java开发组件模型(规范),JPA是其中关于数据持久化的一部分。
1.2 JPA优势
标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
容器级特性的支持
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
1.3 JPA需明确
- JPA是一套ORM规范,hibernate实现了JPA规范
- hibernate有自己独立的ORM数据库操作方式,也实现了JPA规范的数据库操作方式
- 在数据持久化操作中,hibernate和JPA操作都要回
第二章:JPA入门(示例说明)
2.1 需求介绍(单表操作)
以客户类为例,基于JPA注解方式进行实体类和数据库表的关系映射,从而进行CRUD操作。
2.2 JPA环境搭建
2.2.1 第一步:导jar包
导入所有hibernate的jar包,并加入红框中的jar包。
2.2.2 第二步:创建配置文件
要求:在src下面的META-INF文件夹下面创建一个名称为persistence.xml的文件。
配置文件的内容:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<!--配置持久化单元-->
<!--name属性:用于定义持久化单元的名字,可任意,可为空
transaction-type属性:指定事务类型(可选)
取值:JTA:java transaction api(多用于集群场景)
RESOURCE_LOACL:本地代码事务
-->
<persistence-unit name="myJPAUnit" transaction-type="RESOURCE_LOCAL">
<!--JPA规范提供商-->
<!-- javax.persistence.PersistenceProvider接口的一个实现类(可选) -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!--显式列出实体类,有多少写多少class-->
<class>cn.itcast.domain.Customer</class>
<class>cn.itcast.domain.LinkMan</class>
<class>cn.itcast.domain.SysUser</class>
<class>cn.itcast.domain.SysRole</class>
<!--厂商专有属性(可选) 我们用的Hibernate,后面都是复制hibernate.cfg.xml中的配置信息-->
<properties><!--注意标签形式的不同-->
<!--生成DDL策略-->
<property name="hibernate.hbm2ddl.auto" value="update"/>
<!--数据库的链接信息-->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/hibernate_jpa"/>
<property name="hibernate.connection.username" value="root"></property>
<property name="hibernate.connection.password" value="1234"/>
<!--指定方言-->
<property name="dialect" value="org.hibernate.dialect.MySQL5Dialect"></property>
<!--是否显示SQL语句-->
<property name="hibernate.show_sql" value="false"></property>
<!--是否格式化sql语句-->
<property name="hibernate.format_sql" value="true"></property>
</properties>
</persistence-unit>
</persistence>
2.2.3 第三步:编写工具类,用于获取JPA操作数据库对象
此处编写JPA工具类,用于获取JPA操作数据库对象。类似于HibernateUtil类,用于获取Session对象(操作数据库对象)。
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class JPAUtil{
//JPA的实体管理工厂,相当于hibernate的SessionFactory
private static EntityManagerFactory em;
//使用静态代码块赋值
static{
//通过Persistence静态类的静态方法获得实体管理工厂类,参数注意和persistence.xml的
//persistence-unit标签的name属性名
em = Persistence.createEntityManagerFactory("myJPAUnit");
}
/**
*使用管理器工厂生产一个管理器对象
*/
public EntityManager getEntityManager(){
return em.createEntityManager();
}
}
2.2.4 第四步:编写实体类进行注解配置
/**
* 客户的实体类
*/
@Entity
@Table(name="cus_customer")
public class Customer implements Serializable{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@column(name="cust_id")
private Long custId;
@column(name="cust_name")
private String custName;
@column(name="cust_source")
private String custSource;
@column(name="cust_industry")
private String custIndustry;
@column(name="cust_level")
private String custLevel;
@column(name="cust_address")
private String custAddress;
@column(name="cust_phone")
private String custPhone;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
@Override
public String toString() {
return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
+ ", custPhone=" + custPhone + "]";
}
}
2.3 常用注解说明
@Entity
作用:指定当前类是实体类。写上此配置用于生成SessionFactory/EntityManager时,加载映射配置
@Table
作用:指定实体类对应的数据库表
属性:
name:指定数据库表的名字
@id
指定当前字段是主键
@GenerateValue
作用:指定主键生成方式。
属性:strategy:指定主键生成策略。JPA支持四种生成策略。
@Column
作用:指定实体类属性和数据库表之间的对应关系
属性:
name:指定数据库表列名
unique:是否唯一
nullable:是否可以为空
inserttable:是否允许插入
updateable:是否可以更新
columnDefinition:定义建表时创建此列DDL
secondaryTable:从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字
2.4 主键生成策略
通过annotation(注解)来映射hibernate实体的,基于annotation的hibernate主键标识为@Id,其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法。JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO。具体说明如下:
2.4.1 IDENTITY:主键由数据库自动生成(自动增长型)
用法:
@id
@GenerateValue(strategy=GenerateType.IDENTITY)
private Long custId;
2.4.2 SEQUENCE:根据底层数据库序列来生成主键,条件是数据库支持序列
用法:
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="payablemoney_seq")
@SequenceGenerator(name="payablemoney_seq",sequenceName="seq_payment")
说明:
@SequenceGenerator源码中的定义
@Target({TYPE,METHOD,FIELD})
@Retention(RUNTIME)
public@interface SequenceGenerator{
String name();
String sequenceName() default"";
int initialValue() default 0;
int allocationSize() default 50;
}
name:表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中。
sequenceName:属性表示生成策略用到的数据库序列名称。
initialValue:表示主键初识值,默认为0。
allocationSize:表示每次主键值增加的大小,例如设置1,则表示每次插入新记录后自动加1,默认为50。
2.4.3 AUTO:主键由程序控制
用法:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
2.4.4 TABLE:使用一个特定数据库表格保存主键
//用法:
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator="payablemoney_gen")
@TableGenerator(name = "pk_gen",
table="tb_generator",
pkColumnName="gen_name",
valueColumnName="gen_value",
pkColumnValue="PAYABLEMOENY_PK",
allocationSize=1
)
//这里应用表tb_generator,定义为 :
/*
CREATE TABLE tb_generator (
id NUMBER NOT NULL,
gen_name VARCHAR2(255) NOT NULL,
gen_value NUMBER NOT NULL,
PRIMARY KEY(id)
)
*/
//插入纪录,供生成主键使用:
//INSERT INTO tb_generator(id, gen_name, gen_value)VALUES (1,PAYABLEMOENY_PK', 1);
//在主键生成后,这条纪录的value值,按allocationSize递增。
@TableGenerator的定义:
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface TableGenerator {
String name();
String table() default "";
String catalog() default "";
String schema() default "";
String pkColumnName() default "";
String valueColumnName() default "";
String pkColumnValue() default "";
int initialValue() default 0;
int allocationSize() default 50;
UniqueConstraint[] uniqueConstraints() default {};
}
/*
其中属性说明:
name:
表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中。
table:
表示表生成策略所持久化的表名,例如,这里表使用的是数据库中的“tb_generator”。
catalog和schema:
具体指定表所在的目录名或是数据库名。
pkColumnName:
属性的值表示在持久化表中,该主键生成策略所对应键值的名称。例如在“tb_generator”中将“gen_name”作为主键的键值
valueColumnName:
属性的值表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加。例如,在“tb_generator”中将“gen_value”作为主键的值
pkColumnValue:
属性的值表示在持久化表中,该生成策略所对应的主键。例如在“tb_generator”表中,将“gen_name”的值为“CUSTOMER_PK”。
initialValue:
表示主键初识值,默认为0。
allocationSize:
表示每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50。
UniqueConstraint:
与@Table标记中的用法类似。
*/
2.5 JPA的CRUD操作
2.5.1 保存
/**
*保存一个实体:persist()
*/
public void test1(){
//定义对象
private Customer c = new Customer();
c.setCustName("传智学院");
c.setCustLevel("VIP客户");
c.setCustSource("网络");
c.setCustIndustry("IT教育");
c.setCustAddress("昌平区北七家镇");
c.setCustPhone("010-84389340");
EntityManager em = null;
EntityTransaction tx = null;
try{
//获得实体管理对象
em = JPAUtil.getEntityManager();
//获得事务对象
tx = em.getTransaction();
//开启事务
tx.begin();
//执行操作
em.persist(c);
//提交事务
tx.commit();
}catch(Exception e){
//回滚事务
tx.rollback();
}finally{
//释放资源
em.close();
}
}
2.5.2 修改
利用快照机制进行修改更新操作:
/**
*修改一个实体
* 利用快照机制,修改em缓存中的对象,事务提交时自动和快照缓存比较并进行一致性修改
*
*/
public void test2(){
//定义对象
EntityManager em = null;
EntityTransaction = null;
try{
//获得实体类管理对象
em = JPAUtil.getEntityManager();
//获得事务对象
tx = em.getTransaction();
//开启事务
tx.begin();
//修改操作(先获得实体对象,在进行修改)
Customer c = em.find(Customer.class,6L);
c.setCustName("试验修改操作");
//提交事务
tx.commit();
}catch(Exception e){
//回滚事务
tx.rollback();
e.printStackTrace();
}finally{
//释放资源
em.close();
}
}
利用merge方法进行修改:
/**
*修改操作:merge方法
*
*/
public void test3(){
//定义对象
EntityManager em = null;
EntityTransaction tx = null;
try{
em = JPAUtil.getEntityManager();
tx = em.getTransaction();
//修改操作
Customer c1 = em.find(Customer.class,6L);
c1.setName("修改之merge方法");
em.clear();//把c1对象从缓存中清除出去
em.merge(c1);
tx.commit();
}catch(Exception e){
tx.rollback();
e.printStackTrace();
}finally{
em.close();
}
}
2.5.3 删除
/**
*删除操作:remove()
*
*/
public void test4(){
//定义对象
EntityManager em = null;
EntityTransaction tx = null;
try{
em = JPAUtil.getEntityManager();
tx = em.getTransaction();
//修改操作
Customer c2 = em.find(Customer.class,6L);
em.remove(c2);
tx.commit();
}catch(Exception e){
tx.rollback();
e.printStackTrace();
}finally{
em.close();
}
}
2.5.4 查询一个
立即加载(find):执行命令,立即加载查询对象。相当于hibernate的get()方法
/**
*查询一个:立即加载
*
*/
public void test5(){
//定义对象
EntityManager em = null;
EntityTransaction tx = null;
try{
em = JPAUtil.getEntityManager();
tx = em.getTransaction();
//修改操作
Customer c2 = em.find(Customer.class,6L);
tx.commit();
}catch(Exception e){
tx.rollback();
e.printStackTrace();
}finally{
em.close();
}
}
延迟加载(getReference):执行命令,加载查询对象id(主键)。当用到该对象属性时,查询其他属性。相当于hibernate的load()方法。
/**
*查询一个:延迟加载
*
*/
public void test5(){
//定义对象
EntityManager em = null;
EntityTransaction tx = null;
try{
em = JPAUtil.getEntityManager();
tx = em.getTransaction();
//修改操作
Customer c2 = em.getReference(Customer.class,6L);
tx.commit();
}catch(Exception e){
tx.rollback();
e.printStackTrace();
}finally{
em.close();
}
}
2.5.5 查询所有
/**
* 查询所有
* 涉及的对象:
* Query(注意:不是Hibernate的Query)
* 如何获取:
* 使用EntityManager的createQuery(String JPQL)方法;
* 参数的含义
* JPQL:jpa query language
* JPQL的写法:
* 表名使用实体类名称替代
* 列名使用实体类属性名称替代
* 不能使用*号。查询所有,需要在from关键字后面的类名上加别名
* 例如: select c from Customer c
* 查询条件可以使用?作为参数占位符。
* 给占位符赋值时,占位符索引位置从1开始
* 获取结果集的方法
* getResultList():查询结果是一个List集合
* getSingleResult():查询结果是一个对象
*/
@Test
public void testFindAll(){
//定义对象
EntityManager em=null;
EntityTransaction tx=null;
try{
//获取实体管理对象
em=JPAUtil.getEntityManager();
//获取事务对象
tx=em.getTransaction();
//开启事务
tx.begin();
/*执行操作
*1.创建Query对象,输入jpql语句
*2.条件查询指定条件
*3.执行查询操作
*/
Query query = em.createQuery("select c from Customer c where custName like ?");
query.setParament(1,"%学院%");
List list = query.getResultList();
//提交事务
tx.commit();
for(Object o : list){
System.out.println(o);
}
}catch(Exception e){
//回滚事务
tx.rollback();
e.printStackTrace();
}finally{
//释放资源
em.close();
}
}
第三章:JPA多表映射
3.1 一对多关系-常用注解配置
3.1.1 @OneToMany:
作用:建立一对多关系映射
属性:
targetEntityClass:many一方的实体类名称
mappedBy:指定many一方实体类中引用one一方对象的名称。其中,取值一方的实体类放弃获得维护关系的权利。
cascade:指定要使用的级联操作
fetch:指定是否延迟加载
orphanRemoval:是否使用孤儿删除
具体解释:
fetch (fetch = FetchType.LAZY)
属性是该实体的加载方式,FetchType是枚举类型,值有两种:LAZY和EAGER。
一的一方,FetchType默认是LAZY,表示关联数据不同时加载。
多的一方,FetchType默认是EAGER,表示关联数据同时加载。
mappedBy (mappedBy = "properties")
属性用于双向关联实体时使用,用在关系的维护端指定关系的被维护端。
用在多的一方:@OneToMany(mappedBy = "properties")orphanRemoval(boolean orphanRemoval() default false;)
属性作用是删除孤立记录,即外键为空的类型,默认为false。该属性为true时会根据外键执行级联删除,因为当你删除productType的记录时,会使product表的记录外键被删除变为孤立记录,该属性进而将孤立记录删除掉。但jpa实际执行的语句是先删除product表中的记录,后删除productType表中的记录。
cascade(CascadeType[] cascade() default {};)
级联属性,默认为空。该属性其实是一个值为枚举类型CascadeType的数组,在jpa的CascadeType枚举类型里面。
PERSIST(级联保存操作)
MERGE(合并(merge=save+update)
REMOVE(级联删除操作)
REFRESH(级联刷新操作)
DETACH(级联分离操作)(jpa2.0新加入的变量)
ALL(所有级联操作)
作者:关外野游
链接:https://www.jianshu.com/p/64e2fce1a5d0
来源:简书
3.1.2 @ManyToOne:
作用:建立多对一的关系映射
属性:
targetEntityClass:one一方的实体类名称
cascade:指定要使用的级联操作
fetch:是否延迟加载
optional:关联是否可选。如果设置为false,则必须存在非空关系。
3.1.3 @JoinColumn
作用:用于定义主键字段和外键字段的对应关系。该注解和mappedBy属性互斥。用于many一方
属性:
name:指定外键字段名称
referenceColumnName:指定引用主表的主键字段名称
unique:是否唯一。默认不唯一
nullable:是否允许为空。默认允许
insertable:是否允许插入。默认允许
updatable:是否允许更新。默认允许
columnDefinition:列的定义信息
3.2 一对多关系-实体类配置示例
3.2.1 客户配置示例
/**
* 客户的实体类
* 明确使用的注解都是JPA规范的
* 所以导包都要导入javax.persistence包下的
*
*/
@Entity//表示当前类是一个实体类
@Table(name="cst_customer")//建立当前实体类和表之间的对应关系
public class Customer implements Serializable {
@id//表示该属性是主键
@GeneratedValue(strategy=GenerationType.IDENTITY)//指定主键生成策略
@Column(name="cust_id")
private Long custId;
@Column(name="cust_name")//指定和数据库表中的cust_name列对应
private String custName;
@Column(name="cust_source")//指定和数据库表中的cust_source列对应
private String custSource;
@Column(name="cust_industry")//指定和数据库表中的cust_industry列对应
private String custIndustry;
@Column(name="cust_level")//指定和数据库表中的cust_level列对应
private String custLevel;
@Column(name="cust_address")//指定和数据库表中的cust_address列对应
private String custAddress;
@Column(name="cust_phone")//指定和数据库表中的cust_phone列对应
private String custPhone;
@OneToMany(targetEntityClass=LinkMan.class,mappedBy="customer",fetch=FetchType.LAZY,cascade=CascadeType.ALL,)
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public Set<LinkMan> getLinkmans() {
return linkmans;
}
public void setLinkmans(Set<LinkMan> linkmans) {
this.linkmans = linkmans;
}
@Override
public String toString() {
return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource
+ ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress
+ ", custPhone=" + custPhone + "]";
}
}
3.2.2 联系人配置示例
/**
* 联系人的实体类(数据模型)
*/
@Entity
@Table(name="cst_linkman")
public class LinkMan implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="lkm_id")
private Long lkmId;
@Column(name="lkm_name")
private String lkmName;
@Column(name="lkm_gender")
private String lkmGender;
@Column(name="lkm_phone")
private String lkmPhone;
@Column(name="lkm_mobile")
private String lkmMobile;
@Column(name="lkm_email")
private String lkmEmail;
@Column(name="lkm_position")
private String lkmPosition;
@Column(name="lkm_memo")
private String lkmMemo;
//多对一关系映射:多个联系人对应客户
@ManyToOne(targetEntityClass=Customer.class,fetch=FetchType.EAGER)
@JoinColumn(name="lkm_cust_id",referenceColumnName="cust_id")
private Customer customer;//用它的主键,对应联系人表中的外键
public Long getLkmId() {
return lkmId;
}
public void setLkmId(Long lkmId) {
this.lkmId = lkmId;
}
public String getLkmName() {
return lkmName;
}
public void setLkmName(String lkmName) {
this.lkmName = lkmName;
}
public String getLkmGender() {
return lkmGender;
}
public void setLkmGender(String lkmGender) {
this.lkmGender = lkmGender;
}
public String getLkmPhone() {
return lkmPhone;
}
public void setLkmPhone(String lkmPhone) {
this.lkmPhone = lkmPhone;
}
public String getLkmMobile() {
return lkmMobile;
}
public void setLkmMobile(String lkmMobile) {
this.lkmMobile = lkmMobile;
}
public String getLkmEmail() {
return lkmEmail;
}
public void setLkmEmail(String lkmEmail) {
this.lkmEmail = lkmEmail;
}
public String getLkmPosition() {
return lkmPosition;
}
public void setLkmPosition(String lkmPosition) {
this.lkmPosition = lkmPosition;
}
public String getLkmMemo() {
return lkmMemo;
}
public void setLkmMemo(String lkmMemo) {
this.lkmMemo = lkmMemo;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
@Override
public String toString() {
return "LinkMan [lkmId=" + lkmId + ", lkmName=" + lkmName + ", lkmGender=" + lkmGender + ", lkmPhone="
+ lkmPhone + ", lkmMobile=" + lkmMobile + ", lkmEmail=" + lkmEmail + ", lkmPosition=" + lkmPosition
+ ", lkmMemo=" + lkmMemo + "]";
}
}
3.3 多对多关系-常用注解配置
3.3.1 @ManyToMany
作用:建立多对多关系映射
属性:
mappedBy:仅一方实体类使用
cascade:配置级联操作。
fetch:配置是否采用延迟加载。
targetEntity:配置目标的实体类。映射多对多的时候不用写。
3.3.2 @JoinTable
作用:针对中间表的配置(仅维护方实体类使用)
属性:
name:中间表名字
joinColumns:用来使 中间表外键字段 关联 当前实体类的主键字段
inverseJoinColumn:用来使 中间表的另一个外键字段 关联 对方表的主键字段
3.3.3 @JoinColumn
作用:用于定义主键字段和外键字段的对应关系
属性:
name:指定外键字段的名称
referencedColumnName:指定引用主表的主键字段名称
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
insertable:是否允许插入。默认值允许。
updatable:是否允许更新。默认值允许。
columnDefinition:列的定义信息。
3.4 多对多关系-实体类配置示例
3.4.1 用户配置示例
/**
* 用户的数据模型
*/
@Entity
@Table(name="sys_user")
public class SysUser implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="user_id")
private Long userId;
@Column(name="user_code")
private String userCode;
@Column(name="user_name")
private String userName;
@Column(name="user_password")
private String userPassword;
@Column(name="user_state")
private String userState;
//多对多关系映射
@ManyToMany(mappedBy="users")//因为放弃了维护,所以@JoinTable在对方实体类配置
private Set<SysRole> roles = new HashSet<SysRole>(0);
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserState() {
return userState;
}
public void setUserState(String userState) {
this.userState = userState;
}
public Set<SysRole> getRoles() {
return roles;
}
public void setRoles(Set<SysRole> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "SysUser [userId=" + userId + ", userCode=" + userCode + ", userName=" + userName + ", userPassword="
+ userPassword + ", userState=" + userState + "]";
}
}
3.4.2 角色配置示例
/**
* 角色的数据模型
*/
@Entity
@Table(name="sys_role")
public class SysRole implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="role_id")
private Long roleId;
@Column(name="role_name")
private String roleName;
@Column(name="role_memo")
private String roleMemo;
//多对多关系映射
@ManyToMany
@JoinTable(name="user_role_rel",//中间表的名称
//中间表user_role_rel字段关联sys_role表的主键字段role_id
joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")},
//中间表user_role_rel的字段关联sys_user表的主键user_id
inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")}
)
private Set<SysUser> users = new HashSet<SysUser>(0);
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleMemo() {
return roleMemo;
}
public void setRoleMemo(String roleMemo) {
this.roleMemo = roleMemo;
}
public Set<SysUser> getUsers() {
return users;
}
public void setUsers(Set<SysUser> users) {
this.users = users;
}
@Override
public String toString() {
return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]";
}
}
第四章:JPA多表操作
4.1 一对多关系增删改查操作
4.1.1 保存操作
保存原则:先保存主表,在保存从表。
JPA注解的配置方式:不涉及多一条update语句的问题
/**
* 保存操作
* 需求:
* 保存一个客户和一个联系人
* 要求:
* 创建一个客户对象和一个联系人对象
* 建立客户和联系人之间关联关系(双向一对多的关联关系)
* 先保存客户,再保存联系人
*/
@Test
public void test1(){
//创建客户和联系人对象
Customer c = new Customer();//瞬时态
c.setCustName("TBD云集中心");
c.setCustLevel("VIP客户");
c.setCustSource("网络");
c.setCustIndustry("商业办公");
c.setCustAddress("昌平区北七家镇");
c.setCustPhone("010-84389340");
LinkMan l = new LinkMan();//瞬时态
l.setLkmName("TBD联系人");
l.setLkmGender("male");
l.setLkmMobile("13811111111");
l.setLkmPhone("010-34785348");
l.setLkmEmail("98354834@qq.com");
l.setLkmPosition("老师");
l.setLkmMemo("还行吧");
//建立双方关联关系
c.getLinkMans.add(l);
l.setCustomer(c);
//获得实体管理对象和事务对象
EntityManager em = JPAUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
//开启事务
tx.begin();
//进行保存操作
em.persist(c);//先保存主表,然后保存从表
em.persist(l);
tx.commit();
}
4.1.2 修改操作
/**
* 更新操作
* 需求:
* 更新客户
* 要求:
* 创建一个新的联系人对象
* 查询id为1的客户
* 建立联系人和客户的双向一对多关联关系
* 更新客户
* 问题:
* 当更新一个持久态对象时,它关联了一个瞬时态的对象。执行更新
* 注解配置:什么都不会做
* 解决办法:
* 配置级联保存更新
*/
@Test
public void test2(){
//创建联系人对象
LinkMan l = new LinkMan();//瞬时态
l.setLkmName("TBD联系人test");
l.setLkmGender("male");
l.setLkmMobile("13811111111");
l.setLkmPhone("010-34785348");
l.setLkmEmail("98354834@qq.com");
l.setLkmPosition("老师");
l.setLkmMemo("还行吧");
//获取JPA操作对照
EntityManager em = JPAUtil.getEntityManager();
//获取JPA事务对象
EntityTransaction tx= em.getTransaction();
//开启事务
tx.begin();
//查询id为1的客户
Customer c1 = em.find(Customer.class, 1L);
//建立双向关联关系
c1.getLinkmans().add(l);
//l.setCustomer(c1);
//更新客户有两种方式
//第一种:使用jpa提交事务自动更新机制
//第二种:使用merge方法更新
//此处我们使用JPA提交事务自动更新
tx.commit();
}
解决没有保存联系人的问题,配置级联保存更新:
注解的配置方式:
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL)
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
4.1.3 删除操作
/**
* 删除操作
* 删除从表数据:可以随时任意删除。
* 删除主表数据:
* 有从表数据引用
* 1、不能删除
* 2、如果还想删除,使用级联删除
* 没有从表数据引用:随便删
*
* 在实际开发中,级联删除请慎用!(在一对多的情况下)
*/
@Test
public void test3(){
//获取JPA操作对照
EntityManager em = JPAUtil.getEntityManager();
//获取JPA事务对象
EntityTransaction tx= em.getTransaction();
//开启事务
tx.begin();
//查询id为1的客户
Customer c1 = em.find(Customer.class, 2L);
//删除id为1的客户
em.remove(c1);
tx.commit();
}
级联删除的配置:
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL) //用CascadeType.REMOVE也可以
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
4.2 多对多关系增删改查操作
4.2.1 保存操作
JPA注解的配置方式:不涉及保存失败的问题:
/**
* 需求:
* 保存用户和角色
* 要求:
* 创建2个用户和3个角色
* 让1号用户具有1号和2号角色(双向的)
* 让2号用户具有2号和3号角色(双向的)
* 保存用户和角色
*/
@Test
public void test1(){
//创建对象
SysUser u1 = new SysUser();
u1.setUserName("用户1");
SysUser u2 = new SysUser();
u2.setUserName("用户2");
SysRole r1 = new SysRole();
r1.setRoleName("角色1");
SysRole r2 = new SysRole();
r2.setRoleName("角色2");
SysRole r3 = new SysRole();
r3.setRoleName("角色3");
//建立关联关系
u1.getRoles().add(r1);
u1.getRoles().add(r2);
r1.getUsers().add(u1);
r2.getUsers().add(u1);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
r2.getUsers().add(u2);
r3.getUsers().add(u2);
//获取JPA操作对照
EntityManager em = JPAUtil.getEntityManager();
//获取JPA事务对象
EntityTransaction tx= em.getTransaction();
//开启事务
tx.begin();
em.persist(u1);
em.persist(u2);
em.persist(r1);
em.persist(r2);
em.persist(r3);
tx.commit();
}
4.2.2 删除操作
在多对多映射配置中不能出现双向级联删除的配置,无论注解还是XML配置
/**
* 删除操作
* 在多对多的删除时,双向级联删除根本不能配置
* 禁用
* 如果配了的话,如果数据之间有相互引用关系,可能会清空所有数据
*/
@Test
public void test2(){
//获取JPA操作对照
EntityManager em = JPAUtil.getEntityManager();
//获取JPA事务对象
EntityTransaction tx= em.getTransaction();
//开启事务
tx.begin();
SysUser u1 = em.find(SysUser.class,3L);
em.remove(u1);
tx.commit();
}
第五章:JPA其他
5.1 在JPA中使用C3P0数据源
第一步:拷贝hibernate中使用C3P0所必须的3个jar包
如下图所示:
第二步:在persistence.xml配置文件中配置
<!-- 配置使用C3P0数据源 -->
<property name="hibernate.connection.provider_class"
value="org.hibernate.connection.C3P0ConnectionProvider"/>
验证是否配置成功
/**
* 测试是否绑定数据源成功
*/
@Test
public void testConnection(){
EntityManager em = JPAUtil.getEntityManager();
Session s = em.unwrap(Session.class);
s.doWork(new Work() {
@Override
public void execute(Connection conn) throws SQLException {
System.out.println(conn);
}
});
}
输出结果如下图:
5.2 JAP和hibernate中操作数据的方法对照
操作 | Hibernate中的方法 | JPA中的方法 | 说明 |
保存操作 | save(Object entity) | persist(Object entity) | 共同点:都是把临时态对象转成了持久态。 区别: 提供者不一样: save方法是hibernate提供的。 persist方法是JPA规范提供的。 在没有事务的情况下: save会去数据库中保存,hibernate提供了一个内置的事务来执行。 persist什么都不会做。 |
更新操作 | update (Object entity) | merge (Object entity) | Hibernate和jpa都可以利用快照机制,不调用任何方法去更新。 Update方法在更新时,如果遇到一级缓存已经包含了一个相同OID的对象会报错。merge则可以执行成功。 |
删除操作 | delete (Object entity) | remove (Object entity) | 都是删除一个实体 |
查询一个操作 | get (Class clazz,Serializable id) load(Class clazz,Serializable id) | find(Class clazz,Object id) getReerence(Class clazz,Object id) | get和find都是立即加载。load和getReference一样都是延迟加载。 |
查询所有操作 | Query:使用HQL语句查询 | Query:使用JPQL查询 | 查询语句的形式不一样。 |
查询返回唯一结果操作 | uniqueResult() | getSingleResult() | 查询都是返回一个唯一的结果。 |
5.3 关于JTA和RESOURCE_LOCAL的区别:
5.3.1 JTA
JTA事务(Java Transaction API)是J2EE规范中有关事务的标准。它是容器级别的事务,只能运行在J2EE服务器中。它的最大优势是可以支持分布式的事务,如果系统采用的是分布式的数据库,那么只能选择JTA管理EntityManager事务。
使用JTA管理EntityManager事务时,需要注意以下几个问题。
1、JTA事务只能运行在J2EE的环境中,即EJB容器中和Web容器中;而在J2SE环境中只能使用RESOURCE_LOCAL管理事务。
2、容器托管的EntityManager对象只能采用JTA的事务,而不能采用RESOURCE_LOCAL事务。
5.3.2 RESOURCE_LOCAL
RESOURCE_LOCAL事务数据库本地的事务。它是数据库级别的事务,只能针对一种数据库,不支持分布式的事务。对于中小型的应用,可以采用RESOURCE_LOCAL管理EntityManager事务。
使用RESOURCE_LOCAL管理EntityManager事务时需要注意以下几个问题。
1、在J2SE环境中,只能使用RESOURCE_LOCAL管理EntityManager事务,并且EntityManager对象是以应用托管方式获得的。
2、代码中使用RESOURCE_LOCAL管理事务时,要通过调用EntityManager的getTransaction()方法获得本地事务。