Hibernate中有两套实现数据库数据操作的方式 : hibernate前3天讲解的都是 : hibernate自己的操作方式(纯XML配置文件的方式) 另一种方式是基于JPA的操作方式(通过注解的方式替代之前的部分XML) JPA相关概念: 1.1JPA概述 : (java的持久化规范(规范即接口)) 全称是 : Java Persistence API. 是SUN公司推出的一套基于ORM的规范.hibernate框架中提供了JPA的实现. JAP通过JDK5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中. 1.2JPA要明确的 a. JPA是一套ORM规范(算是一个接口),hibernate实现了JPA规范(算是一个实现类). b. hibernate中有自己的独立ORM操作数据库方式,也有JPA规范实现的操作数据库方式. c. 在数据库增删改查操作中,我们hibernate和JPA的操作都要会. JPA和hibernate的关系? JPA是接口,hibernate是实现. 所有的ORM框架都可以去实现JPA接口,通过JPA提供的一些接口来操作数据库的数据. JPA的使用 : JPA是通过注解的方式来描述,对象和表的映射关系. 之前的对象和表的映射关系配置是通过XML,今天要替换成注解的方式. 注释 : 给程序员看的. 注解 : 给程序来使用的. 为什么要出现注解? 一开始就是为了替代所有的XML配置文件. 工作中两种方式结合使用 : 配置文件 + 注解 如果是需要常常改动的程序 : 用配置文件. 如果是定义好了不需要更改的程序 : 注解. 2.1 2 JPA的环境搭建 1 hibernate的环境(16个包) 2 JPA的环境(1个包) 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属性用于定义持久化单元的名字 (name必选,空值也合法); transaction-type 指定事务类型(可选) 取值: JTA:默认值 RESOURCE_LOCAL --> <persistence-unit name="myPersistUnit" transaction-type="RESOURCE_LOCAL"> <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 name="hibernate.connection.password" value="1234" /> <!-- 指定方言 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" /> <!-- 是否显示SQL语句 --> <property name="hibernate.show_sql" value="false" /> <!-- 是否格式化SQL语句 --> <property name="hibernate.format_sql" value="true" /> </properties> </persistence-unit> </persistence> 2.2.3 编写工具类,用于获取JPA的操作数据库对象 public class HibernateUtils { //JPA的实体管理器工厂 : 相当于Hibernate的SessionFactory private static EntityManagerFactory em; //使用静态代码块赋值 static { //加载一次配置文件 //注意 : 该方法参数必须和persistence.xml中persistence-unit标签name属性取值一致 em = Persistence.createEntityManagerFactory("aaa"); } /* * 使用管理器工厂生产一个管理器对象 */ //相当于获取连接 public static EntityManager getEntityManager() { return em.createEntityManager(); } } jpa 的批量查询方式 : 类似咋们之前学习的query方式查询 : 1 : qr.getResultList() ; 类似之前的qr.list(); 2 : hibernate对于占位符? 以前是从0开始,jpa是从1开始. 3 : 聚合 qr.getSingleResult(); 类似之前的uniqueResult(); 2.2.4 编写实体类,并使用注解配置 //级联保存 (保存客户的同时把关联的联系人给保存了) //jpa的注解里面 @OneToMany 添加属性cascade = CascadeType.PERSIST_STORE //根据一的一方保存多的一方的数据. // 级联保存 (保存联系人的同时把关联的客户给保存了) // jpa的注解里面 @ManyToOne 添加属性cascade=CascadeType.PERSIST //jpa的一对多没有普通删除 // 级联删除 // jpa的注解里面 @OneToMany 添加属性cascade=CascadeType.REMOVE (All) /** * 客户的实体类 */ @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") 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 作用 : 指定当前字段是主键. @GeneratedValue 作用 : 指定主键的生成方式.JPA的主键生成方式 属性 : 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.1IDENTITY:主键由数据库自动生成(主要是自动增长型) 用法: @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long custId; 2.4.2SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。 用法: @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.3AUTO:主键由程序控制。 用法: @Id @GeneratedValue(strategy = GenerationType.AUTO) 2.5JPA的CRUD操作 2.5.1保存 /** * 保存一个实体 */ @Test public void testAdd(){ //定义对象 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(); e.printStackTrace(); }finally{ //释放资源 em.close(); } } 2.5.2修改 /** * 修改 */ @Test public void testUpdate(){ //定义对象 EntityManager em=null; EntityTransaction tx=null; try{ //获取实体管理对象 em=JPAUtil.getEntityManager(); //获取事务对象 tx=em.getTransaction(); //开启事务 tx.begin(); //执行操作 Customer c1 = em.find(Customer.class, 1L); c1.setCustName("江苏传智学院"); //提交事务 tx.commit(); //使用JPA中快照机制实现更新 }catch(Exception e){ //回滚事务 tx.rollback(); e.printStackTrace(); }finally{ //释放资源 em.close(); } } merge方法实现修改 @Test public void testMerge(){ //定义对象 EntityManager em=null; EntityTransaction tx=null; try{ //获取实体管理对象 em=JPAUtil.getEntityManager(); //获取事务对象 tx=em.getTransaction(); //开启事务 tx.begin(); //执行操作 Customer c1 = em.find(Customer.class, 6L); c1.setCustName("江苏传智学院"); em.clear();//把c1对象从缓存中清除出去 em.merge(c1); //提交事务 tx.commit(); }catch(Exception e){ //回滚事务 tx.rollback(); e.printStackTrace(); }finally{ //释放资源 em.close(); } } 2.5.3删除 /** * 删除 */ @Test public void testRemove(){ //定义对象 EntityManager em=null; EntityTransaction tx=null; try{ //获取实体管理对象 em=JPAUtil.getEntityManager(); //获取事务对象 tx=em.getTransaction(); //开启事务 tx.begin(); //执行操作 Customer c1 = em.find(Customer.class, 6L); em.remove(c1); //提交事务 tx.commit(); }catch(Exception e){ //回滚事务 tx.rollback(); e.printStackTrace(); }finally{ //释放资源 em.close(); } } 2.5.4查询一个 /** * 查询一个: * 使用立即加载的策略 */ @Test public void testGetOne(){ //定义对象 EntityManager em=null; EntityTransaction tx=null; try{ //获取实体管理对象 em=JPAUtil.getEntityManager(); //获取事务对象 tx=em.getTransaction(); //开启事务 tx.begin(); //执行操作 Customer c1 = em.find(Customer.class, 1L); //提交事务 tx.commit(); System.out.println(c1); //输出查询对象 }catch(Exception e){ //回滚事务 tx.rollback(); e.printStackTrace(); }finally{ //释放资源 em.close(); } } 查询实体的缓存问题 @Test public void testGetOne(){ //定义对象 EntityManager em=null; EntityTransaction tx=null; try{ //获取实体管理对象 em=JPAUtil.getEntityManager(); //获取事务对象 tx=em.getTransaction(); //开启事务 tx.begin(); //执行操作 Customer c1 = em.find(Customer.class, 1L); Customer c2 = em.find(Customer.class, 1L); System.out.println(c1 == c2);//输出结果是true,EntityManager也有缓存 //提交事务 tx.commit(); System.out.println(c1); }catch(Exception e){ //回滚事务 tx.rollback(); e.printStackTrace(); }finally{ //释放资源 em.close(); } } 延迟加载策略的方法: /** * 查询一个: * 使用延迟加载策略 */ @Test public void testLoadOne(){ //定义对象 EntityManager em=null; EntityTransaction tx=null; try{ //获取实体管理对象 em=JPAUtil.getEntityManager(); //获取事务对象 tx=em.getTransaction(); //开启事务 tx.begin(); //执行操作 Customer c1 = em.getReference(Customer.class, 1L); //提交事务 tx.commit(); System.out.println(c1); }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(); //执行操作 Query query = em.createQuery("select c from Customer c where custName like ? "); query.setParameter(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(); } } 3.1一对多关系映射 3.1.1常用注解 3.1.1.1@OneToMany: 作用: 建立一对多的关系映射 属性: targetEntityClass:指定多的方的类的字节码 mappedBy:指定从表实体类中引用主表对象的名称。 cascade:指定要使用的级联操作 fetch:指定是否采用延迟加载 orphanRemoval:是否使用孤儿删除 3.1.1.2@ManyToOne 作用: 建立多对一的关系 属性: targetEntityClass:指定一的一方实体类字节码 cascade:指定要使用的级联操作 fetch:指定是否采用延迟加载 optional:关联是否可选。如果设置为false,则必须始终存在非空关系。 3.1.1.3@JoinColumn 作用: 用于定义主键字段和外键字段的对应关系。 属性: name:指定外键字段的名称 referencedColumnName:指定引用主表的主键字段名称 unique:是否唯一。默认值不唯一 nullable:是否允许为空。默认值允许。 insertable:是否允许插入。默认值允许。 updatable:是否允许更新。默认值允许。 columnDefinition:列的定义信息。 3.1.2配置代码 3.1.2.1客户配置 /** * 客户的实体类 * 明确使用的注解都是JPA规范的 * 所以导包都要导入javax.persistence包下的 * */ @Entity//表示当前类是一个实体类 @Table(name="cst_customer")//建立当前实体类和表之间的对应关系 public class Customer implements Serializable { @Id//表明当前私有属性是主键 @GeneratedValue(strategy=GenerationType.IDENTITY)//指定主键的生成策略 @Column(name="cust_id")//指定和数据库表中的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(targetEntity=LinkMan.class,mappedBy="customer") 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.1.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(targetEntity=Customer.class) @JoinColumn(name="lkm_cust_id",referencedColumnName="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.2多对多关系映射 3.2.1常用注解: 3.2.1.1@ManyToMany 作用: 用于映射多对多关系 属性: cascade:配置级联操作。 fetch:配置是否采用延迟加载。 targetEntity:配置目标的实体类。映射多对多的时候不用写。 3.2.1.2@JoinTable 作用: 针对中间表的配置 属性: name:配置中间表的名称 joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段 inverseJoinColumn:中间表的外键字段关联对方表的主键字段 3.2.1.3@JoinColumn 作用: 用于定义主键字段和外键字段的对应关系。 属性: name:指定外键字段的名称 referencedColumnName:指定引用主表的主键字段名称 unique:是否唯一。默认值不唯一 nullable:是否允许为空。默认值允许。 insertable:是否允许插入。默认值允许。 updatable:是否允许更新。默认值允许。 columnDefinition:列的定义信息。 3.2.2配置代码 3.2.2.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") 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.2.2.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 + "]"; } } 第4章JPA的多表操作 4.1一对多关系的增删改操作 4.1.1保存操作 保存原则:先保存主表,再保存从表。 /** * 保存操作 * 需求: * 保存一个客户和一个联系人 * 要求: * 创建一个客户对象和一个联系人对象 * 建立客户和联系人之间关联关系(双向一对多的关联关系) * 先保存客户,再保存联系人 */ @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("还行吧"); //建立他们的双向一对多关联关系 l.setCustomer(c); c.getLinkmans().add(l); //获取JPA操作对照 EntityManager em = JPAUtil.getEntityManager(); //获取JPA事务对象 EntityTransaction tx= em.getTransaction(); //开启事务 tx.begin(); //按照要求:先保存客户,再保存联系人(此时符合保存原则:先保存主表,再保存从表) em.persist(c); em.persist(l); tx.commit(); } JPA注解的配置方式:不涉及多一条update语句的问题 4.1.2删除操作 /** * 删除操作 * 删除从表数据:可以随时任意删除。 * 删除主表数据: * 有从表数据引用 * 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保存操作 /** * 需求: * 保存用户和角色 * 要求: * 创建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(); } JPA注解的配置方式:不涉及保存失败的问题: 4.2.2删除操作 /** * 删除操作 * 在多对多的删除时,双向级联删除根本不能配置 * 禁用 * 如果配了的话,如果数据之间有相互引用关系,可能会清空所有数据 */ @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(); } 在多对多映射配置中不能出现双向级联删除的配置,无论注解还是XML配置 5.2JPA和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() 查询都是返回一个唯一的结果。 JPA一对多的注解配置: 案例: // ps: jpa提供的注解都在:javax.persistence包下 @Entity @Table(name="cst_customer") public class Customer { @Id @Column(name="cust_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long cust_id; // '客户编号(主键)', @Column(name="cust_name") private String cust_name; // '客户名称(公司名称)', @Column(name="cust_source") private String cust_source; // '客户信息来源', @Column(name="cust_industry") private String cust_industry; // '客户所属行业', @Column(name="cust_level") private String cust_level; // '客户级别', @Column(name="cust_address") private String cust_address; // '客户联系地址', @Column(name="cust_phone") private String cust_phone; // '客户联系电话 // 有多的一方的集合 /*targetEntity:对方的类型 * mappedBy:自己在对方里的属性名称 (mappedBy写在哪方,哪方意味着放弃外键的维护) * * */ @OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.REMOVE) private Set<LinkMan> linkmans=new HashSet(); @Entity @Table(name="cst_linkman") public class LinkMan { @Id @Column(name="lkm_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long lkm_id;// '联系人编号(主键)', @Column(name="lkm_name") private String lkm_name;// '联系人姓名', @Column(name="lkm_gender") private String lkm_gender;// '联系人性别', @Column(name="lkm_phone") private String lkm_phone;// '联系人办公电话', @Column(name="lkm_mobile") private String lkm_mobile;// '联系人手机', @Column(name="lkm_email") private String lkm_email;// '联系人邮箱', @Column(name="lkm_qq") private String lkm_qq;// '联系人qq', @Column(name="lkm_position") private String lkm_position;// '联系人职位', @Column(name="lkm_memo") private String lkm_memo;// '联系人备注', // 有一的一方的对象 PERSIST @ManyToOne(targetEntity=Customer.class,cascade=CascadeType.PERSIST) /*name:代表着外键字段的名称*/ /*referencedColumnName:指向的主键字段的命名称*/ @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") private Customer customer; 多表对多表的注解配置: @Entity @Table(name="sys_user") public class User { @Id @Column(name="user_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long user_id;// '用户id', @Column(name="user_code") private String user_code;// '用户账号', @Column(name="user_name") private String user_name;// '用户名称', @Column(name="user_password") private String user_password;// '用户密码', @Column(name="user_state") private String user_state;// '1:正常,0:暂停', // 有角色的集合 /*targetEntity:对方的类型 * * * */ @ManyToMany(targetEntity=Role.class,cascade=CascadeType.ALL) /*name: 中间表的名称 joinColumns:自己在中间表的一些配置 inverseJoinColumns:对方在中间表的一些配置*/ @JoinTable(name="sys_user_role", joinColumns={ /*name:自己在中间表的外键字段名称 referencedColumnName:指向自己的主键字段名*/ @JoinColumn(name="user_id",referencedColumnName="user_id") }, inverseJoinColumns={ /*name:对方在中间表的外键字段名称 referencedColumnName:指向的对方的主键字段名称*/ @JoinColumn(name="role_id",referencedColumnName="role_id") }) private Set<Role> roles=new HashSet(); @Entity @Table(name="sys_role") public class Role { @Id @Column(name="role_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Long role_id;// 主键id @Column(name="role_name") private String role_name;// '角色名称', @Column(name="role_memo") private String role_memo;// '备注', // 有用户的集合 /*targetEntity:对方的类型 * mappedBy:自己在对方的属性名 * */ @ManyToMany(targetEntity=User.class,mappedBy="roles") private Set<User> users=new HashSet(); JPA需要在项目src下新建一个META-INF文件夹在文件夹里面配置以下信息: 例如 : <?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_2_0.xsd" version="2.0"> <!-- ps:当前根目录下必须得有一个持久化单元(至少要有一个数据库的连接信息配置) --> <persistence-unit name="aaa"> <!-- 数据库的连接信息 --> <properties> <!-- 必选5项 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.url" value="jdbc:mysql:///hibernate3"></property> <property name="hibernate.connection.username" value="root"></property> <property name="hibernate.connection.password" value="1234"></property> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"></property> <!-- 可选的 --> <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"></property> <property name="hibernate.show_sql" value="true"></property> <property name="hibernate.format_sql" value="true"></property> <property name="hibernate.hbm2ddl.auto" value="update"></property> </properties> </persistence-unit> </persistence> JPA单表操作 : 例如 : package cn.baidu.demo; public class Demo1 { @Test //需求:保存一个客户 public void t1() { // 加载配置文件--返回一个类似Sessionfactory的对象 EntityManagerFactory factory = Persistence.createEntityManagerFactory("aaa"); // 类似session EntityManager em = factory.createEntityManager(); // 获取事务 EntityTransaction tx = em.getTransaction(); // 事务未开启 // 开启事务 tx.begin(); // 保存操作 Customer ct = new Customer(); ct.setCust_name("李冰冰"); em.persist(ct); // 类似save() // 手动提交 tx.commit(); // 释放资源 em.close(); } @Test //查询1 public void t2() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("aaa"); EntityManager em = factory.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 查询 Customer ct = em.find(Customer.class, 1L); // 立即加载 类似get() System.out.println(ct); tx.commit(); em.close(); } @Test //查询2 public void t3() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("aaa"); EntityManager em = factory.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 查询2 Customer ct = em.getReference(Customer.class, 1L); //延迟加载 类似load() System.out.println(ct); tx.commit(); em.close(); } @Test //修改 public void t4() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 修改 Customer ct = em.find(Customer.class, 1L); ct.setCust_name("rose"); em.merge(ct); //类似update tx.commit(); em.close(); } @Test //修改 jpa支持一级缓存? 支持 public void t5() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 修改 Customer ct = em.find(Customer.class, 1L); ct.setCust_name("rose123"); tx.commit(); em.close(); } @Test //删除 public void t6() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); //删除 Customer ct = em.find(Customer.class, 1L); em.remove(ct); // 类似之前的delete() tx.commit(); em.close(); } /// jpa的批量查询方式 // 类似咱们之前学习的query方式 // 1 qr.getResultList(); //类似之前的 qr.list() // 2 hibernate对于占位符?是从0开始 jpa是从1开始 // 3 聚合 qr.getSingleResult(); //类似之前的 qr.uniqueResult(); @Test public void t7() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 全查 Query qr = em.createQuery("from Customer"); List<Customer> list = qr.getResultList(); //类似之前的 qr.list() for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t8() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 条件 Query qr = em.createQuery("from Customer where cust_name like ?"); qr.setParameter(1, "b%"); // 注意:hibernate对于占位符?是从0开始 jpa是从1开始 List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t9() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 分页 Query qr = em.createQuery("from Customer"); qr.setFirstResult(1); qr.setMaxResults(3); List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t10() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 单列 Query qr = em.createQuery("select cust_name from Customer"); List<Object> list = qr.getResultList(); for (Object object : list) { System.out.println(object); } tx.commit(); em.close(); } @Test public void t11() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 多列 Query qr = em.createQuery("select cust_id,cust_name from Customer"); List<Object[]> list = qr.getResultList(); for (Object[] object : list) { System.out.println(Arrays.toString(object)); } tx.commit(); em.close(); } @Test public void t12() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 投影 Query qr = em.createQuery("select new Customer(cust_id,cust_name) from Customer"); List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t13() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 排序 Query qr = em.createQuery("from Customer order by cust_id desc"); List<Customer> list = qr.getResultList(); for (Customer customer : list) { System.out.println(customer); } tx.commit(); em.close(); } @Test public void t14() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 聚合 Query qr = em.createQuery("select avg(cust_id) from Customer"); Object obj = qr.getSingleResult(); System.out.println(obj); tx.commit(); em.close(); } } JPA一对多表的操作 : /* 一对多的操作*/ public class Demo2 { @Test // 保存一的客户3个联系人 public void t1() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = new Customer(); ct.setCust_name("马总"); LinkMan l1 = new LinkMan(); l1.setLkm_name("大秘"); LinkMan l2 = new LinkMan(); l2.setLkm_name("中秘"); LinkMan l3 = new LinkMan(); l3.setLkm_name("小秘"); // 双向关联 ct.getLinkmans().add(l1); ct.getLinkmans().add(l2); ct.getLinkmans().add(l3); l1.setCustomer(ct); l2.setCustomer(ct); l3.setCustomer(ct); //保存 em.persist(ct); em.persist(l1); em.persist(l2); em.persist(l3); tx.commit(); em.close(); } @Test // 级联保存 (保存客户的同时把关联的联系人给保存了) // jpa的注解里面 @OneToMany 添加属性cascade=CascadeType.PERSIST public void t2() // 根据一的一方保存多的一方的数据(掌握) { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = new Customer(); ct.setCust_name("马总"); LinkMan l1 = new LinkMan(); l1.setLkm_name("大秘"); LinkMan l2 = new LinkMan(); l2.setLkm_name("中秘"); LinkMan l3 = new LinkMan(); l3.setLkm_name("小秘"); // 双向关联 ct.getLinkmans().add(l1); ct.getLinkmans().add(l2); ct.getLinkmans().add(l3); l1.setCustomer(ct); l2.setCustomer(ct); l3.setCustomer(ct); //保存 em.persist(ct); /*em.persist(l1); em.persist(l2); em.persist(l3);*/ tx.commit(); em.close(); } @Test // 级联保存 (保存联系人的同时把关联的客户给保存了) // jpa的注解里面 @ManyToOne 添加属性cascade=CascadeType.PERSIST public void t3() // 根据多的一方保存一的一方的数据(不常用) { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = new Customer(); ct.setCust_name("马总"); LinkMan l1 = new LinkMan(); l1.setLkm_name("大秘"); LinkMan l2 = new LinkMan(); l2.setLkm_name("中秘"); LinkMan l3 = new LinkMan(); l3.setLkm_name("小秘"); // 双向关联 ct.getLinkmans().add(l1); ct.getLinkmans().add(l2); ct.getLinkmans().add(l3); l1.setCustomer(ct); l2.setCustomer(ct); l3.setCustomer(ct); //保存 /*em.persist(ct);*/ em.persist(l1); em.persist(l2); em.persist(l3); tx.commit(); em.close(); } @Test // 普通删除 public void t4() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = em.find(Customer.class, 4L); em.remove(ct); tx.commit(); em.close(); } @Test // 级联删除 // jpa的注解里面 @OneToMany 添加属性cascade=CascadeType.REMOVE (All) public void t5() //根据一的一方删除关联的多的一方的所有数据(掌握) { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer ct = em.find(Customer.class, 4L); em.remove(ct); tx.commit(); em.close(); } } JPA多表对多表的操作 : package cn.baidu.demo; import javax.persistence.CascadeType; import javax.persistence.EntityManager; import javax.persistence.EntityTransaction; import javax.persistence.ManyToMany; import org.junit.Test; import cn.baidu.domain.Role; import cn.baidu.domain.User; import cn.baidu.utils.JPAutils; /* 多对多的操作*/ public class Demo3 { @Test //普通保存 保存2个用户 3个角色 public void t1() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user1 = new User(); user1.setUser_name("jack"); User user2 = new User(); user2.setUser_name("rose"); Role r1 = new Role(); r1.setRole_name("员工"); Role r2 = new Role(); r2.setRole_name("班主任"); Role r3 = new Role(); r3.setRole_name("助教"); // 双向关联 user1.getRoles().add(r1); user1.getRoles().add(r3); user2.getRoles().add(r1); user2.getRoles().add(r2); r1.getUsers().add(user1); r1.getUsers().add(user2); r2.getUsers().add(user2); r3.getUsers().add(user1); // 保存 em.persist(user1); em.persist(user2); em.persist(r1); em.persist(r2); em.persist(r3); tx.commit(); em.close(); } // jpa多对多的级联操作 // 级联保存: 保存用户的同时把关联的角色给保存了(不用) // @ManyToMany 添加cascade=cascade=CascadeType.PERSIST @Test public void t2() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user1 = new User(); user1.setUser_name("jack"); User user2 = new User(); user2.setUser_name("rose"); Role r1 = new Role(); r1.setRole_name("员工"); Role r2 = new Role(); r2.setRole_name("班主任"); Role r3 = new Role(); r3.setRole_name("助教"); // 双向关联 user1.getRoles().add(r1); user1.getRoles().add(r3); user2.getRoles().add(r1); user2.getRoles().add(r2); r1.getUsers().add(user1); r1.getUsers().add(user2); r2.getUsers().add(user2); r3.getUsers().add(user1); // 保存 em.persist(user1); em.persist(user2); /*em.persist(r1); em.persist(r2); em.persist(r3);*/ tx.commit(); em.close(); } @Test // 普通删除(常用) public void t3() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = em.find(User.class, 1L); em.remove(user); tx.commit(); em.close(); } @Test // 级联删除(避免去使用) public void t4() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = em.find(User.class, 2L); em.remove(user); tx.commit(); em.close(); } // jpa的用户角色分配 // 添加角色 // 删除角色 // 修改角色 @Test public void t5() { EntityManager em = JPAutils.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // 获取用户 User user = em.find(User.class, 3L); // 获取班主任 Role rl = em.find(Role.class, 6L); //给用户添加 user.getRoles().add(rl); tx.commit(); em.close(); } } 总结: JPA的作用? 给所有的orm框架提供了一套接口 好处: 所有的ORM框架只要实现了这个JPA接口,用来操作数据库数据的方式和方法以及注解都一致了 jpa的环境搭建: 在hibernate的环境基础上多加一个包--hibernate-entitymanager-5.0.7.Final.jar 单表的映射 @Entity 实体类 @Table(name="cst_customer") 与表的映射 @Id 指定OID属性 @Column(name="cust_id") 指定映射的字段 @GeneratedValue(strategy=GenerationType.IDENTITY) 指定主键的生成策略 crud: persist() ----保存 find() : 立即加载 getReference():延迟加载 -----单条数据的oid查询 merge() ---修改 remove() ---删除 批量查询: 类似之前的query方式 一对多: 一: @OneToMany(targetEntity=LinkMan.class,mappedBy="customer") 多: @ManyToOne(targetEntity=LinkMan.class) 一对多的关系配置: @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") 多对多: 多(被动): @ManyToMany(targetEntity=User.class,mappedBy="roles") 多(主动): @ManyToMany(targetEntity=Role.class) 多对多的关系配置: @JoinTable(name="中间表的名称",joinColumns="自己在中间表的配置(数组)" inverseJoinColumns="对方在中间表的配置(数组)") 级联: cascade=CascadeType.ALL 做级联保存以及级联删除 cascade=CascadeType.PERSIST 只做级联保存 cascade=CascadeType.REMOVE 只做级联删除