笔记5
JPA单表操作
可以省去配置每个实体类的.xml文件,用注解的方式在实体类中直接说明就可以了
1.主要配置:
注意:此配置文件必须在src的根目录下边,文件名必须是perisistence.xml
其中的具体配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<!-- 配置持久化单元,可以配置多个,但是名称不能一样
name:用来指定持久化单元名称
transaction-type:事务类型
JTA:是Javatransaction api
RESOURCE_LOCAL:本地代码事务(用这个)
-->
<persistence-unit name="myJPAUnit"transaction-type="RESOURCE_LOCAL">
<!-- JPA配置的提供商,可以不写-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 指定JPA注解的实体类位置,可以不写-->
<class>com.wbs.domain.Customer</class>
<!-- 链接数据库相关的配置,都是hibernate的,只需要把hibernate之前的主配置文件中的内容拷贝即可 -->
<properties>
<!--第一部分:数据库的链接配置 -->
<property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver"/>
<property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
<property name="hibernate.connection.username" value="system"/>
<property name="hibernate.connection.password" value="wbs19950305"/>
<property name="hibernate.dialectt" value="org.hibernate.dialect.Oracle8iDialec"/>
<!-- 第二部分:可选配置 -->
<property name="hibernate.show_sql" value="true" /> <!-- 是否显示hibernate生成的sql语句 -->
<property name="hibernate.format_sql" value="true"/><!-- 是否使用格式化输出sql语句到控制台 -->
<!-- 配置hibernate使用何种语句生成DDL语句 -->
<property name="hibernate.hbm2ddl.auto" value="update"/><!-- update表示检测实体类映射配置和表结构是否一致,如果不一致,更新表结构 -->
<!-- 设置hibernate的连接池供应商-->
<!-- <propertyname="hibernate.connection.provider_class" value=
org.hibernate.connection.C3P0ConnectionProvider/> -->
</properties>
</persistence-unit>
</persistence>
2.获取JPA操作数据库的对象:
其中的createEntityManagerFactory()方法中的参数必须要和persistence.xml中的持久化单元名称一致。
//相当于SessionFactory
privatestatic EntityManagerFactory factory;
static {
factory=Persistence.createEntityManagerFactory("myJPAUnit");
}
//获取JPA操作数据库的对象
publicstatic EntityManagercreateEntityManager(){
returnfactory.createEntityManager();
}
3.具体的操作
/**
* JPA入门案例
* JPA单表操作
* @author bo
*
*/
publicclass JPAtest {
//保存
@Test
publicvoid test1(){
Customer c=new Customer();
c.setCusName("JPA客户1");
//1.获取EntityManager对象
EntityManager em=JPAUtils.createEntityManager();
//获取事务对象,并开启事务
EntityTransaction tx=em.getTransaction();
tx.begin();
//执行保存操作
em.persist(c);
//提交事务
tx.commit();
//关闭资源
em.close();
}
//查找操作,
@Test
publicvoid test2(){
//find是立即加载
//1.获取EntityManager对象
EntityManager em=JPAUtils.createEntityManager();
//获取事务对象,并开启事务
EntityTransaction tx=em.getTransaction();
tx.begin();
//需要把更新的数据查找出来
Customer c=em.find(Customer.class, 161L);
System.out.print(c);
//提交事务
tx.commit();
//关闭资源
em.close();
}
//更新操作
@Test
publicvoid test3(){
//1.获取EntityManager对象
EntityManager em=JPAUtils.createEntityManager();
//获取事务对象,并开启事务
EntityTransaction tx=em.getTransaction();
tx.begin();
//需要把更新的数据查找出来
Customer c=em.find(Customer.class, 161L);
//修改客户的地址为大连
c.setCusAddress("大连");
//提交事务
tx.commit();
//关闭资源
em.close();
}
//更新的另一种方式
//merge合并并更新
@Test
publicvoid test4(){
//1.获取EntityManager对象
EntityManager em=JPAUtils.createEntityManager();
//获取事务对象,并开启事务
EntityTransaction tx=em.getTransaction();
tx.begin();
//需要把更新的数据查找出来
Customer c=em.find(Customer.class, 162L);
//修改客户的地址为大连
c.setCusAddress("dlmu");
em.merge(c);
//提交事务
tx.commit();
//关闭资源
em.close();
}
//删除操作
@Test
publicvoid test5(){
//1.获取EntityManager对象
EntityManager em=JPAUtils.createEntityManager();
//获取事务对象,并开启事务
EntityTransaction tx=em.getTransaction();
tx.begin();
//需要把更新的数据查找出来
Customer c=em.find(Customer.class, 161L);
em.remove(c);
//提交事务
tx.commit();
//关闭资源
em.close();
}
//查询所有
/**
* 涉及的对象是:JPAQuery
* 如何获取该对象:
* EntityManager的createQuery(Stringjpql)方法
* 参数的含义:
* jpql:Java Persistence Query Language
* 写法和HQL相似,也是把表明换成类名,把字段名换成属性名,
* 它在写查询所有时候不能使用 "from 实体类名"
* 需要使用select关键字
* select c from Customer c //前一个c是别名
*/
@Test
publicvoid test6(){
//1.获取EntityManager对象
EntityManager em=JPAUtils.createEntityManager();
//获取事务对象,并开启事务
EntityTransaction tx=em.getTransaction();
tx.begin();
//获取JPA的QUERY对象
Query query=em.createQuery("selectc from Customer c where cusName like ?1 and cusLevel=?2");
//给占位符赋值
query.setParameter(1,"%大连%");
query.setParameter(2, "23");
//执行方法,获取结果集
List list=query.getResultList();
for(Object o:list){
System.out.println(o);
}
//提交事务
tx.commit();
//关闭资源
em.close();
}
说明:
1. persist()方法相当于是save()操作;
2. remove()对应的是delete();
3. find()方法对应get()方法;
4. getReference()是延迟加载;
5. find()是立即加载;
6. uniqueResult()对应getSingleResult(),返回唯一的结果。
7. merge()和update()相似,但是merge干的活update有些不能干;
8. 下边说明merge和update的不同:
当查询了一个对象,关闭了session,有查询了该对象,有修改了该对象,此时如果用update方法的时候会报错,因为第一次查完后对象的状态转变成了托管态,而在此查询该对象,修改的时候是持久态,此时对象的状态时不一样的,在一级缓存外边还有一个改对象,如果此时更新的话,因为两个的对象的OID是一样的,但是却发生了修改,此时若Update的话,两个对象是不能合并的,只能用merge()方法将其更新,即将两个对象合并了。
/**
* 查询ID为3的客户
* 关闭session(清空了一级缓存和快照)
* 修改id为1的客户的名称为英华5公寓
* 在此获取session
* 再次查询ID为1的客户
* 更新刚才修改的客户
*/
@Test
publicvoid test1(){
Session s=HibernateUtils.openSession();
Transaction tx=s.beginTransaction();
Customer c=s.get(Customer.class, 1L);//持久态
tx.commit();
s.close();
//修改客户信息
c.setCusname("英华5公寓");//托管态
Session s1=HibernateUtils.openSession();
Transaction tx1=s.beginTransaction();
//再次查询
Customer c1=s.get(Customer.class, 1L);//持久态
//更新操作
//s1.update(c);//将托管态转换为持久态,update方法是不行的,必须要用到merge方法才可以的
s1.merge(c);
tx1.commit();
s1.close();
}
JPA一对多表操作
1. 一对多实体类的注解写法:
/**
* 客户的实体类
* 使用的注解都是JPA规范下的,所以导包,都需要导入javax.persistence下的包
*/
@Entity//表明该类是一个实体类
@Table(name="jpa_cus_customer")//建立当前类和数据库表的对应关系
publicclass Customer implements Serializable {
@Id//表明当前字段是一个主键
@Column(name="cust_id")//表明对应的数据库的主键字段是cus_id
@GeneratedValue(strategy=GenerationType.SEQUENCE)//请指定主键生成策略.strategy:使用JPA提供的主键生成策略,此属性用不了。generator:可以使用hibernate中的主键生成策略
private Long cusId;
@Column(name="cust_name")
private String cusName;
@Column(name="cust_source")
private String cusSource;
@Column(name="cust_industry")
private String cusIndustry;
@Column(name="cust_level")
private String cusLevel;
@Column(name="cust_address")
private String cusAddress;
@Column(name="cust_phone")
private String cusPhone;
//一对的哟关系映射:一个客户可以有多个联系人
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer")//mappedBy是映射来自,相当于inverse,即主表不在关心从表的信息,让联系人去维护
private Set<LinkMan> linkmans=new HashSet<LinkMan>(0);
2. JPA实现一对多插入操作
JPA操作值插入了两条记录,而不会有update语句, 因为在主表中已经mappedBy="customer",放弃维护了
/**
* 保存操作
* 创建一个客户和一个联系人
* 建立客户和联系人之间的关系
* 先保存客户,再保存联系人
*/
@Test
publicvoid test1(){
Customer c=new Customer();
LinkMan l=new LinkMan();
c.setCusName("JPA One To ManyCustomer");
l.setLkmName("JPA Many To OneLinkMan");
c.getLinkmans().add(l);
l.setCustomer(c);
EntityManager em=JPAUtils.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
em.persist(c);
em.persist(l);
tx.commit();
em.close();
}
3. JPA的更新操作
此时必须要配置级联操作:想级联操作谁就应该在谁的上边进行注解配置cascade=CsacadeType.PERSIST属性,即保存或者更新客户的同时保存联系人,
但时cascade=CsacadeType.PERSIST只是级联更新
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.PERSIST)//mappedBy是映射来自,相当于inverse,即主表不在关心从表的信息,让联系人去维护
private Set<LinkMan> linkmans=new HashSet<LinkMan>(0);
/**
* 更新操作
* 新建联系人
* 查询ID为186的客户
* 为这个人分配联系人
* 更新客户
*/
@Test
publicvoid test2(){
LinkMan l=new LinkMan();
l.setLkmName("JPA Many To One LinkMan3333");
EntityManager em=JPAUtils.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
Customer c=em.find(Customer.class,185L);
c.getLinkmans().add(l);
l.setCustomer(c);
//em.merge(l);//这句可以不写,因为有快照机制,会自己更新的
tx.commit();
em.close();
}
4.JPA的删除操作
1.删除操作
删除主表:若在客户上边配置了放弃维护,即mappedBy="customer",直接删除指标会报错
此时还想要删除的话,需要配置cascade=CascadeType.DELET或者cascade=CascadeType.ALL就可以删除
需要注意的是:联系人那边(从表)也可以配置cascade=CascadeType.ALL这些东西
@Test
publicvoid test3(){
EntityManager em=JPAUtils.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
Customer c=em.find(Customer.class,185L);
em.remove(c);
tx.commit();
em.close();
}
JPA的查询操作
JPA中也可以使用对象导航查询,也可以设置查询的时机
延迟加载的特点:真正用到该对象的时候才开始查询改对象的属性
1.如果是立即加载,需要在Customer的set集合的注解中加入下边的语句:fetch=FetchType.EAGER,其原理是利用了左外连接查询的方式实现了立即加载。没写是EAGER,即默认是EAGER。LinkMan中也可是设置成立即加载。
@OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.ALL,fetch=FetchType.EAGER)//mappedBy是映射来自,相当于inverse,即主表不在关心从表的信息,让联系人去维护
private Set<LinkMan>linkmans=new HashSet<LinkMan>(0);
下边的查询可以设置成立即加载,也可以设置成延迟加载
/**
* 根据客车讯客户下边的联系人
*/
@Test
publicvoid test1(){
EntityManager em=JPAUtils.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
//1.查询ID为1的客户
Customer c=em.find(Customer.class, 1L);
System.out.println(c);
System.out.print(c.getLinkmans());
tx.commit();
em.close();
}
/**
* 根据联系人查询客户
*/
@Test
publicvoid test2(){
EntityManager em=JPAUtils.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
//1.查询ID为1的客户
LinkMan l=em.find(LinkMan.class, 1L);
System.out.println(l);
Customer c=l.getCustomer();
System.out.print(c);
tx.commit();
em.close();
}
JPA的多对多
1.在角色实体对象中,如果配置了中间表的表名和在中间表中的列明,则在另外多的一方中只需要配置@ManyToMany(mappedBy="users")//谁写mappedBy谁就不在关心创建中间表了,即让另外一方不在关心创建中间表。
@Entity
@Table(name="jpa_sysrole")
publicclass SysRole implements Serializable {
@Id
@Column(name="role_id")
@GenericGenerator(name="aa",strategy="uuid")//声明一个主键生成策略的生成器name:给生成器起名字;strategy:指定Hibernate中包含的主键生成策略
@GeneratedValue(generator="aa")
private Long roleId;
@Column(name="role_name")
private String roleName;
@Column(name="role_memo")
private String roleMemo;
//多对多关系映射,一个角色可以赋给多个用户
@JoinTable(name="jpa_user_role",joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")},//joinColumns写的是当前实体在中间表中的晚间字段
inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")}//inverseJoinColumns写的是对方实体在中间表中的晚间字段
)
private Set<SysUser> users=newHashSet<SysUser>(0);
其中黄色标出的是多对多的关系中特殊的配置,需要注意。
SysUser的主要配置如下,即一方不再关心中间表的创建的事情,只交给一个多方去管。
@ManyToMany(mappedBy="users")//谁写mappedBy谁就不在关心创建中间表了
private Set<SysRole>roles=new HashSet<SysRole>(0);
2.多对多的操作
/**
* JPA的多对多操作
* 保存操作
* 删除操作
*/
publicclass JPADemo1 {
/**
* 保存操作
* 常见两个用户
* 创建三个角色
* 让一号用户具有1,2号角色
* 让2号用户具备2,3号角色
* 保存用户和角色
*/
@Test
publicvoid test1(){
SysUser u1=new SysUser();
u1.setUserName("JPA Many To ManyUser1");
SysUser u2=new SysUser();
u2.setUserName("JPA Many To ManyUser2");
SysRole r1=new SysRole();
r1.setRoleName("JPA Many To ManyRole1");
SysRole r2=new SysRole();
r2.setRoleName("JPA Many To ManyRole2");
SysRole r3=new SysRole();
r3.setRoleName("JPA Many To ManyRole3");
//建立用户角色之间的关系
u1.getRoles().add(r1);
u1.getRoles().add(r2);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
r1.getUsers().add(u1);
r2.getUsers().add(u1);
r2.getUsers().add(u2);
r3.getUsers().add(u2);
EntityManager em=JPAUtils.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
//保存操作
em.persist(u1);
/*em.persist(u2);//如果设置了级联保存,则只需保存一个,其他的与它级联的都会自动保存
em.persist(r1);
em.persist(r2);
em.persist(r3);*/
tx.commit();
em.close();
}
/**
* 删除操作
* 不管是Hibernate中还是JPA中,多对多都不能这样配置,这样会把所有的数据都删干净的
*/
@Test
publicvoid test2(){
EntityManager em=JPAUtils.createEntityManager();
EntityTransaction tx=em.getTransaction();
tx.begin();
//删除操作
SysUser s=em.find(SysUser.class,"asdfasdfasdfasd");//因为此时用的是UUID,必须要写清楚具体的那个用户ID,而不能写出第几个
tx.commit();
em.close();
}
注意:多对多的级联保存配置如下(需要在每一个实体类中配置),下边只写了一个实体类总的cascade的配置,另外的多表的cascade配置都应改如此
@ManyToMany(cascade=CascadeType.ALL)//多对多关系映射,一个角色可以赋给多个用户
//多对多关系映射,一个角色可以赋给多个用户
@JoinTable(name="jpa_user_role",joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")},//joinColumns写的是当前实体在中间表中的晚间字段
inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")})//inverseJoinColumns写的是对方实体在中间表中的晚间字段
private Set<SysUser>users=new HashSet<SysUser>(0);
3.JPA中的c3p0操作
@Test
publicvoid test1(){
//1.获取JPA中的操作对象
EntityManager em=JPAUtils.createEntityManager();
//2.将em转换成session
Session session=em.unwrap(Session.class);
//验证c3p0连接池是否配置成功
//3.执行session的doWork()方法
session.doWork(new Work(){
@Override
publicvoid execute(Connection conn) throws SQLException {
System.out.println(conn.getClass().getName());
}
});
}
但是要导入c3p0的JAR包,而且要在配置文件里面协商c3p0的供应商:
<property name="hibernate.connection.provider_class"
value="org.hibernate.connection.C3P0ConnectionProvider"/>
如果要从JPA中得到Session对象的话,需要对EntityManager进行unwrap()操作(解包装),就可以得到Session对象,而且用doWork()方法可以得到Connection对象,可以对它进行操作。
4. JPA中使用单线程
自己手写单线程代码,想要使用JPA自己的,需要配置Spring使用。
下边是写出单线程的代码:
publicclass JPAUtils {
//相当于SessionFactory
privatestatic EntityManagerFactory factory;
static {
factory=Persistence.createEntityManagerFactory("myJPAUnit");
tl=new ThreadLocal<EntityManager>();
}
//获取JPA操作数据库的对象
/*public static EntityManager createEntityManager(){
returnfactory.createEntityManager();//此时不是单线程的EntityManager
}*/
publicstaticvoid main(String[] args) {
createEntityManager();
}
//自己写代码让始终有一个EntityManager
privatestatic ThreadLocal<EntityManager> tl;
static{
factory=Persistence.createEntityManagerFactory("myJPAUnit");
tl=new ThreadLocal<EntityManager>();
}
publicstatic EntityManager createEntityManager(){
//1.从当前线程上获取EntityManager对象
EntityManager em=tl.get();
if(tl==null){
em=factory.createEntityManager();
tl.set(em);
}
returntl.get() ; //此时是单线程的EntityManager
}
}
下边是测试单线程的代码:
@Test
publicvoid test2(){
EntityManager em1=JPAUtils.createEntityManager();
EntityManager em2=JPAUtils.createEntityManager();
System.out.println(em1==em2);
}
其测试结果为true。说明是一个线程。
Hibernate总结
Hibernate的JPA配置了的话也可以用Hibernate的方式来增删改查