博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Hibernate JPA
阅读量:5740 次
发布时间:2019-06-18

本文共 35170 字,大约阅读时间需要 117 分钟。

hot3.png

1、JPA概述

        

2、JPA中的第一个程序

 

3、JPA中常用的注解(重点)

                  

4、JPA中的CRUD

        

5、JPA中的多表映射的配置

        

6、JPA中多表的增删改

 

7、对象导航查询

  1. JPA相关概念
    1. JPA概述

 

什么时候JPA?JPA它是一套Java持久化规范。那么JPA和我们前面三天所学的Hibernate有什么关系呢?

  1. JPA是一套Java持久化规范;而Hibernate实现了JPA规范。
  2. 在Hibernate中有两套操作数据库的方式,一套是Hibernate自己特有的的方式,前面三天已经学过了;还有一套是实现了JPA规范的方式。

80e465884cecc6cfe1df04633557bfa1fed.jpg

今天我们主要学习Hibernate中实现了JPA规范的操作数据库的方式。

    1. JPA优势

ddb2ccf135216b299e72f53ca9053136348.jpg

    1. 学习JPA要明确的

      a、JPA是一套ORM规范,hibernate实现了JPA规范

         b、hibernate中有自己的独立ORM操作数据库方式,也有JPA规范实现的操作数据库方式。

         c、在数据库增删改查操作中,我们hibernateJPA的操作都要会。

  1. JPA入门
    1. 第一步:创建工程,引入jar包

要搭建一个JPA的开发环境,除了需要导入前面三天所用的Hibernate的jar包以外,还需要导入hibernate-entitymanager.jar包,该jar包可以从如下路径找到:

82c207f97abd362ad6c193121fa0dcd49ba.jpg

完整的jar包如下图所示:

c4a4f58e6b46fcfb2291ba12d4c31bff17f.jpg

注意:今天我们主要学习JPA规范,但是仅仅只有规范是不行的,还得有规范的实现才行,所以才需要导入Hibernate相关的jar包,因为Hibernate就是JPA规范的实现。

    1. 第二步:创建实体类

创建Customer实体类:

 

在实体类上加上JPA注解,取代传统的hbm.xml文件的功能。需要使用到的注解有:

  1. @Entity:如果一个类加上此注解,表示该类是一个实体类。
  2. @Table:指定该类所映射的表,name属性表示该类所映射的表名。如果类名与表名一致,此注解可以省略。
  3. @Id:表示与主键列对应的属性。
  4. @GeneratedValue:表示主键如何生成。strategy属性表示主键的生成策略。GenerationType.IDENTITY表示自动增长。

/**

 * 客户实体类

 * kevin

 */

@Entity

@Table(name="cst_customer")

public class Customer implements Serializable{

 

   private static final long serialVersionUID = 1L;

   @Id

   @Column(name="cust_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   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 + "]";

   }

}

 

    1. 第三步:创建配置文件

1、在src下新建META-INF文件夹,新建persistence.xml文件。

  1. 引入schema约束,可以从hibernate-entitymanager-5.0.7.Final.jar/org/hibernate/jpa/persistence_2_0.xsd中拷贝

  1. 配置提示

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_2_0.xsd"

   version="2.0">

 

   <persistence-unit name="myPersistUnit">

      <!-- 指定JPA规范的实现:Hibernate的实现 (可省略)-->

      <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

      <!-- 指定要被扫描到的实体类 (可省略)-->

      <class>cn.itcast.domain.Customer</class>

      <properties>

         <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>

         <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/hibernate_itheima20"/>

         <property name="hibernate.connection.username" value="root"/>

         <property name="hibernate.connection.password" value="123456"/>

         <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

<property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"></property>

         <property name="hibernate.c3p0.max_size" value="20"></property>

         <property name="hibernate.c3p0.min_size" value="5"></property>

         <property name="hibernate.c3p0.timeout" value="5000"></property>

         <property name="hibernate.c3p0.idle_test_period" value="3000"></property>

         <property name="hibernate.show_sql" value="true"/>

         <property name="hibernate.format_sql" value="true"/>

         <property name="hibernate.hbm2ddl.auto" value="update"/>

      </properties>

   </persistence-unit>

</persistence>

    1. 第四步:编写工具类,用于获取JPA操作数据库的对象

创建JPAUtils工具类,封装获取EntityManager的方法,等价于封装HibernateUtils工具类用于获取Session:

JPAUtils的内容如下:

/**

 * 用于获取JPA操作数据库对象的工具类

 * @author kevin

 */

public class JPAUtils {

 

   //JPA的实体管理器工厂:相当于Hibernte的SessionFactory

   private static final EntityManagerFactory em;

   //使用静态代码块赋值

   static{

      //创建JPA的实体管理器工厂:该方法参数必须和persistence.xml中persistence-unit标签的name属性一致

      em = Persistence.createEntityManagerFactory("myPersistUnit");

   }

  

   /**

    * 获取实例管理器的工具方法

    * @return

    */

   public static EntityManager getEntityManager(){

      return em.createEntityManager();

   }

}

用到的API的解释:

EntityManagerFactory:它是JPA规范中生产EntityManager的工厂,要保证一个工程中只有一个EntityManagerFactory。作用等价于SessionFactory

EntityManager:实体管理器,用来做CRUD的,作用等价于Session

PersistenceJPA规范中的工具类,用于获取EntityManagerFactory的;

    1. 第五步:编写测试类

创建单元测试类TestJPA:

TestJPA内容如下:

/**

    * 测试JPA注解里的保存

    */

   @Test

   public void test1(){

      Customer customer = new Customer();

      customer.setCustName("蔚来汽车");

      customer.setCustIndustry("人工智能");

      customer.setCustSource("网络营销");

      customer.setCustLevel("VIP客户");

      customer.setCustAddress("北京");

      customer.setCustPhone("0108650123");    

//获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

      //保存对象

      em.persist(customer);

      //提交事务

      tx.commit();

      //释放资源

      em.close();

   }

用到的API的解释:

EntityTransactionJPA规范中的事务接口,用来管理JPA规范中的事务,作用等价于Transaction;

EntityManager.persist():用来保存对象的;作用等价于session.save();

  1. 常用注解及主键生成策略
    1. 常用注解说明

@Entity

    作用:指定当前类是实体类。写上此注解用于在创建SessionFactory/EntityManager时,加载映射配置。

@Table

    作用:指定实体类和表之间的对应关系。

    属性:

        name:指定数据库表的名称

@Id

    作用:指定当前字段是主键。

@GeneratedValue

    作用:指定主键的生成方式。JPA的主键生成方式详解见2.4小节的说明。

    属性:

        strategy :指定主键生成策略。JPA支持四种生成策略,具体介绍看3.2小节。

@Column

    作用:指定实体类属性和数据库表里列之间的对应关系

    属性:

        name:指定数据库表的列名称。

        unique:是否唯一  

            nullable:是否可以为空  
            inserttable:是否可以插入  
            updateable:是否可以更新  
            columnDefinition: 定义建表时创建此列的DDL  
            secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字。  

    1. 主键生成策略

基于annotation的hibernate主键标识为@Id, 

其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法。

JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO。具体说明如下:

      1. IDENTITY:主键由数据库自动生成(自动增长型) 

用法:

    @Id  

    @GeneratedValue(strategy = GenerationType.IDENTITY) 

    private Long custId;

      1. 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。

      1. AUTO:根据底层数据库自动选择一种策略

如果底层数据库是mysql,JPA是采用一个特定的表来保存主键。

用法:

    @Id  

    @GeneratedValue(strategy = GenerationType.AUTO)  

把Customer的主键生成策略改为AUTO,内容如下:

@Entity

@Table(name="cst_customer")

public class Customer implements Serializable{

 

   private static final long serialVersionUID = 1L;

   @Id

   @Column(name="cust_id")

   @GeneratedValue(strategy=GenerationType.AUTO)

   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 + "]";

   }

 

 

}

此时,向客户表中保存一个对象:Hibernate会自动创建一张hibernate_sequence表,用于存储下一个主键,每次需要获取主键时,就查询hibernate_sequence,把查询到的结果作为主键插入到表中,再把hibernate_sequence表中的主键值+1,测试结果如下:

      1. TABLE:使用一个特定的数据库表格来保存主键(了解)

用法:

@Id  

@GeneratedValue(strategy = GenerationType.TABLE)  

Customer的主键生成策略改为TABLE,内容如下:

@Entity

@Table(name="cst_customer")

public class Customer implements Serializable{

 

   private static final long serialVersionUID = 1L;

   @Id

   @Column(name="cust_id")

   @GeneratedValue(strategy=GenerationType.TABLE)

   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 + "]";

   }

 

 

}

此时,向客户表中保存一个对象:Hibernate会自动创建一张hibernate_sequences表,存储下一个主键值,当初次获取主键值时,先查询hibernate_sequences,此时该表中没有数据,于是,向该表中插入一行初始数据,值为1,再把该表中的主键值+1.

  1. JPA注解的CRUD
    1. 保存

@Test

   public void test1(){

      Customer customer = new Customer();

      customer.setCustName("蔚来汽车");

      //获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

      //保存对象

      em.persist(customer);

      //提交事务

      tx.commit();

      //释放资源

      em.close();

}

    1. 查询一个

/**

    * 查询一个

    */

   @Test

   public void test2(){

      //获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

      Customer customer = em.find(Customer.class, 1L);

      System.out.println(customer);

      //提交事务

      tx.commit();

      //释放资源

      em.close();

   }

 

JPA也是有缓存的:

/**

    * 查询一个:缓存问题

    */

   @Test

   public void test2Cache(){

      //获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

      Customer customer1 = em.find(Customer.class, 1L);

      Customer customer2 = em.find(Customer.class, 1L);

      System.out.println(customer1==customer2);

      //提交事务

      tx.commit();

      //释放资源

      em.close();

   }

 

JPA中也是有延迟加载的:

/**

    * 查询一个:延迟加载的问题

    */

   @Test

   public void test2Lazy(){

      //获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

      Customer customer = em.getReference(Customer.class, 1L);//不发sql语句

      System.out.println(customer);//真正要用的时候,才发送select语句

      //提交事务

      tx.commit();

      //释放资源

      em.close();

   }

    1. 查询所有

/**

    * 查询所有:利用JPQL来查询

 * JPQLJava Persistence Query Language,中文含义是Java持久化查询语言

sql中可以给表起别名,例如:select * from cst_customer c

JPQL语法规定:要给类加别名,例如select c from Customer c,表示查询表里有数的数据

*

    */

   @Test

   public void test3(){

      //获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

      Query query = em.createQuery("select c from Customer c where c.custName like ?");

      query.setParameter(1, "%张%");

      List<Customer> list = query.getResultList();

      for(Customer c:list){

         System.out.println(c);

      }

      //提交事务

      tx.commit();

      //释放资源

      em.close();

   }

细节:JPA中的Query设置参数的位置是从1开始的。

    1. 更新

/**

    * 修改

    */

   @Test

   public void update(){

      //获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

     

      Customer customer = em.find(Customer.class, 1L);

      customer.setCustName("腾讯科技");

      //提交事务

      tx.commit();//使用JPA中快照机制实现更新

      //释放资源

      em.close();

   }

 

    1. 删除

/**

    * 删除

    */

   @Test

   public void delete(){

      //获取JPA中实体管理器

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开启事务

      tx.begin();

     

      Customer customer = em.find(Customer.class, 2L);

      em.remove(customer);

      //提交事务

      tx.commit();//使用JPA中快照机制实现更新

      //释放资源

      em.close();

   }

  1. JPA多表映射
    1. 一对多关系映射
      1. 常用注解
        1. @OneToMany

作用:

    建立一对多的关系映射

属性:

    targetEntityClass:指定多的多方的类的字节码

    mappedBy:指定从表实体类中引用主表对象的名称。让主表(一方)放弃外键维护

    cascade:指定要使用的级联操作

    fetch:指定是否采用延迟加载

    orphanRemoval:是否使用孤儿删除

 

        1. @ManyToOne

作用:

    建立多对一的关系

属性:

    targetEntityClass:指定一的一方实体类字节码

    cascade:指定要使用的级联操作

    fetch:指定是否采用延迟加载

    optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

        1. @JoinColumn

作用:

    用于定义主键字段和外键字段的对应关系。

属性:

    name:指定外键字段的名称

    referencedColumnName:指定引用主表的主键字段名称

    unique:是否唯一。默认值不唯一

    nullable:是否允许为空。默认值允许。

    insertable:是否允许插入。默认值允许。

    updatable:是否允许更新。默认值允许。

    columnDefinition:列的定义信息。

      1. 配置代码

创建客户和联系人实体类,实体类的设计同之前的代码:

 

        1. 客户实体类的配置

/**

 * 客户实体类

 * @author kevin

 */

@Entity

@Table(name="cst_customer")

public class Customer implements Serializable{

 

   private static final long serialVersionUID = 1L;

   @Id

   @Column(name="cust_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   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;

  

   //mappedBy属性:表示由多方(联系人)来维护主键,值就是多方(联系人)中一方的属性名

   @OneToMany(mappedBy="customer")

   private Set<LinkMan> linkMans = new HashSet<LinkMan>();

   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 + "]";

   }

 

}

        1. 联系人实体类的配置

/**

 * 联系人实体类

 * @author kevin

 */

@Entity

@Table(name="cst_linkman")

public class LinkMan implements Serializable {

 

   private static final long serialVersionUID = 1L;

  

   @Id

   @Column(name="lkm_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   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

   @JoinColumn(name="lkm_customer_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 + "]";

   }

  

}

        1. 测试注解配置的正确性

在测试之前,先修改persistence.xml中的hibernte.hbm2ddl.auto的值为create;

在TestJPA中新建单元测试方法如下:

@Test

   public void test2(){

      EntityManager em = JPAUtils.getEntityManager();

   }

    1. 多对多关系映射

创建用户和角色实体类,实体类的设计同之前的代码:

      1. 常用注解
        1. @ManyToMany

作用:

    用于映射多对多关系

属性:

    cascade:配置级联操作。

    fetch:配置是否采用延迟加载。

    targetEntity:配置目标的实体类。映射多对多的时候不用写。

        1. @JoinTable

作用:

    针对中间表的配置

属性:

    nam:配置中间表的名称

    joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段                     

    inverseJoinColumn:中间表的外键字段关联对方表的主键字段

        1. @JoinColumn

作用:

    用于定义主键字段和外键字段的对应关系。

属性:

    name:指定外键字段的名称

    referencedColumnName:指定引用主表的主键字段名称

    unique:是否唯一。默认值不唯一

    nullable:是否允许为空。默认值允许。

    insertable:是否允许插入。默认值允许。

    updatable:是否允许更新。默认值允许。

    columnDefinition:列的定义信息。

 

      1. 配置代码
        1. 用户实体类的配置

/**

 * 用户实体类

 * @author kevin

 */

@Entity

@Table(name="sys_user")

public class SysUser implements Serializable {

 

  

   private static final long serialVersionUID = 1L;

  

   @Id

   @Column(name="user_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   private Long userId;

   @Column(name="user_name")

   private String userName;

   @Column(name="user_memo")

   private String userMemo;

   //多对多关系映射

   @ManyToMany

@JoinTable(name="user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))

   private Set<SysRole> roles = new HashSet<SysRole>();

   public Long getUserId() {

      return userId;

   }

   public void setUserId(Long userId) {

      this.userId = userId;

   }

   public String getUserName() {

      return userName;

   }

   public void setUserName(String userName) {

      this.userName = userName;

   }

   public String getUserMemo() {

      return userMemo;

   }

   public void setUserMemo(String userMemo) {

      this.userMemo = userMemo;

   }

   public Set<SysRole> getRoles() {

      return roles;

   }

   public void setRoles(Set<SysRole> roles) {

      this.roles = roles;

   }

   @Override

   public String toString() {

      return "SysUser [userId=" + userId + ", userName=" + userName + ", userMemo=" + userMemo + "]";

   }

 

}

 

        1. 角色实体类的配置

/**

 * 角色实体类

 * @author kevin

 */

@Entity

@Table(name="sys_role")

public class SysRole implements Serializable {

 

   private static final long serialVersionUID = 1L;

  

   @Id

   @Column(name="role_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   private Long roleId;

   @Column(name="role_name")

   private String roleName;

   @Column(name="role_memo")

   private String roleMemo;

   //一个角色对应多个用户

   @ManyToMany(mappedBy="roles")

   private Set<SysUser> users = new HashSet<SysUser>();

   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 + "]";

   }

}

        1. 测试注解配置的正确性

在测试之前,先修改persistence.xml中的hibernte.hbm2ddl.auto的值为create;

在TestJPA中新建单元测试方法如下:

@Test

   public void test2(){

      EntityManager em = JPAUtils.getEntityManager();

   }

 

 

小结:

@OneToMany

@ManyToOne

@JoinColumn

@ManyToMany

@JoinTable

 

  1. JPA多表操作
    1. 一对多关系的增删改操作
      1. 保存操作

我们先看一对多中的普通保存:

/**

    * 一对多保存

    * 建立双向关系

    */

   @Test

   public void test1(){

      Customer customer = new Customer();

      customer.setCustName("客户1");

      LinkMan linkMan = new LinkMan();

      linkMan.setLkmName("联系人1");

      //建立客户和联系人的双向关系,不会有多余的update语句,因为我们在一方(客户)加了mappedBy,放弃了外键维护权

      customer.getLinkMans().add(linkMan);

      linkMan.setCustomer(customer);

     

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      em.persist(customer);

      em.persist(linkMan);

     

      tx.commit();

      em.close();

   }

如果想级联保存,在Customer实体类的@OneToMany注解中加上级联保存属性:

//一个客户有多个联系人

//mappedBy属性:表示由多方(联系人)来维护主键,值就是多方(联系人)中一方的属性名

@OneToMany(mappedBy="customer",cascade={

CascadeType.PERSIST})

修改Customer实体类代码如下:

@Entity

@Table(name="cst_customer")

public class Customer implements Serializable{

   private static final long serialVersionUID = 1L;

   @Id

   @Column(name="cust_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   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;

  

   //mappedBy属性:表示由多方(联系人)来维护主键,值就是多方(联系人)中一方的属性名

   @OneToMany(mappedBy="customer",cascade=CascadeType.PERSIST)

   private Set<LinkMan> linkMans = new HashSet<LinkMan>();

   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 + "]";

   }

 

  

}

 

修改保存测试代码:只需要保存客户即可:

/**

    * 一对多级联保存:保存客户级联保存联系人

    * 在Customer实体类的OneToMany注解中加cascade=CascadeType.PERSIST

    *

    */

   @Test

   public void test2(){

      Customer customer = new Customer();

      customer.setCustName("客户1");

      LinkMan linkMan = new LinkMan();

      linkMan.setLkmName("联系人1");

      //建立客户和联系人的双向关系,不会有多余的update语句,因为我们在一方(客户)加了mappedBy,放弃了外键维护权

      customer.getLinkMans().add(linkMan);

      linkMan.setCustomer(customer);

     

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      em.persist(customer);

     

      tx.commit();

      em.close();

   }

      1. 删除操作

这里直接演示删除客户级联删除联系人。

如果想级联删除,可以在Customer实体类这边配置级联删除

提示:如果想配置多个级联操作,可以这样来配置:cascade={CascadeType.PERSIST,CascadeType.REMOVE}

修改Customer实体类如下:

@Entity

@Table(name="cst_customer")

public class Customer implements Serializable{

   private static final long serialVersionUID = 1L;

   @Id

   @Column(name="cust_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   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;

  

   //mappedBy:指定一方放弃外键维护权

@OneToMany(mappedBy="customer",cascade={CascadeType.PERSIST,CascadeType.REMOVE})

   private Set<LinkMan> linkMans = new HashSet<LinkMan>();

   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 + "]";

   }

 

}

删除测试代码如下:

/**

    * 一对多级联删除:删除客户,级联删除联系人

    *

    */

   @Test

   public void test3(){

     

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      Customer customer = em.find(Customer.class, 1L);

     

      em.remove(customer);

      tx.commit();

      em.close();

}

      1. 修改操作

/**

    * 一对多的更新

    * 需求:让2号联系人属于2号客户

    */

   @Test

   public void test6(){

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      //先查询id为2的联系人

      LinkMan linkMan = em.find(LinkMan.class, 2L);

      //再查询id为2的客户

      Customer customer = em.find(Customer.class, 2L);

      //重新建立关系

      linkMan.setCustomer(customer);

      tx.commit();

      em.close();

}

 

    1. 多对多关系的增删改操作
      1. 保存操作

先看多对多中的普通保存:

@Test

   public void test1(){

      SysUser user1 = new SysUser();

      user1.setUserName("张三");

     

      SysUser user2 = new SysUser();

      user2.setUserName("李四");

     

      SysRole role1 = new SysRole();

      role1.setRoleName("角色1");

     

      SysRole role2 = new SysRole();

      role2.setRoleName("角色2");

     

      SysRole role3 = new SysRole();

      role3.setRoleName("角色3");

     

      user1.getRoles().add(role1);

      user1.getRoles().add(role2);

      role1.getUsers().add(user1);

      role2.getUsers().add(user1);

     

      user2.getRoles().add(role2);

      user2.getRoles().add(role3);

      role2.getUsers().add(user2);

      role3.getUsers().add(user2);

     

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction txem.getTransaction();

      tx.begin();

      em.persist(user1);

      em.persist(user2);

      em.persist(role1);

      em.persist(role2);

      em.persist(role3);

      tx.commit();

      em.close();

   }

如果想级联保存,在User实体类中设置级联保存:

@Entity

@Table(name = "sys_user")

public class SysUser implements Serializable {

 

   private static final long serialVersionUID = 1L;

  

   @Id

   @Column(name="user_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   private Long userId;

   @Column(name="user_name")

   private String userName;

   @Column(name="user_memo")

   private String userMemo;

   //多对多关系映射

   @ManyToMany(cascade={CascadeType.PERSIST})

@JoinTable(name="user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))

   private Set<SysRole> roles = new HashSet<SysRole>();

   public Long getUserId() {

      return userId;

   }

   public void setUserId(Long userId) {

      this.userId = userId;

   }

   public String getUserName() {

      return userName;

   }

   public void setUserName(String userName) {

      this.userName = userName;

   }

   public String getUserMemo() {

      return userMemo;

   }

   public void setUserMemo(String userMemo) {

      this.userMemo = userMemo;

   }

   public Set<SysRole> getRoles() {

      return roles;

   }

   public void setRoles(Set<SysRole> roles) {

      this.roles = roles;

   }

   @Override

   public String toString() {

      return "SysUser [userId=" + userId + ", userName=" + userName + ", userMemo=" + userMemo + "]";

   }

 

}

 

因为设置了级联保存,所以只需要保存用户即可:

@Test

   public void test1(){

      SysUser user1 = new SysUser();

      user1.setUserName("用户1");

      SysUser user2 = new SysUser();

      user2.setUserName("用户2");

     

      SysRole role1 = new SysRole();

      role1.setRoleName("角色1");

      SysRole role2 = new SysRole();

      role2.setRoleName("角色2");

      SysRole role3 = new SysRole();

      role3.setRoleName("角色3");

     

      user1.getRoles().add(role1);

      user1.getRoles().add(role2);

      user2.getRoles().add(role2);

      user2.getRoles().add(role3);

      role1.getUsers().add(user1);

      role2.getUsers().add(user1);

      role2.getUsers().add(user2);

      role3.getUsers().add(user2);

     

      //获取EntityManager

      EntityManager em = JPAUtils.getEntityManager();

      //获取事务

      EntityTransaction tx = em.getTransaction();

      //开始事务

      tx.begin();

      //保存操作

      em.persist(user1);

      em.persist(user2);

      /*em.persist(role1);

      em.persist(role2);

      em.persist(role3);*/

      //提交事务

      tx.commit();

      //释放资源

      em.close();

}

      1. 删除操作

先看直接删除用户的情况:

/**

    * 测试删除:直接删除用户

    * 结果:先删除中间表中与该用户相关的信息,再删除用户

    */

   @Test

   public void test2(){

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      SysUser user = em.find(SysUser.class, 1L);

      em.remove(user);

     

      tx.commit();

      em.close();

   }

如果想级联删除,可以在用户一方配置级联删除:

//一个用户对应多个角色

   @ManyToMany(cascade=CascadeType.REMOVE)

@JoinTable(name="user_role",joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id"))

   private Set<SysRole> roles = new HashSet<SysRole>();

 

 

/**

    * 测试多对多级联删除:删除用户级联删除角色

    */

   @Test

   public void test3(){

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      SysUser user = em.find(SysUser.class, 1L);

      em.remove(user);

     

      tx.commit();

      em.close();

   }

   测试发现:报错,原因同xml配置。所以,在多对多中一般不配置级联删除

      1. 修改操作

/**

    * 多对多的更新

    * 需求:把用户1中的角色1换成角色3

    */

   @Test

   public void test5(){

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

      //先查询id为1的用户

      SysUser user = em.find(SysUser.class, 1L);

      //再查询id为1的角色

      SysRole role1 = em.find(SysRole.class, 1L);

      //再查询id为3的角色

      SysRole role3 = em.find(SysRole.class, 3L);

      //先移除角色1

      user.getRoles().remove(role1);

      //在添加角色3

      user.getRoles().add(role3);

     

      tx.commit();

      em.close();

   }

    1. JPA里的对象导航查询

明确:导航查询的意义和xml的意义一样

      1. 查询一方时,延迟加载多方数据

/**

    * 查询客户,也要把客户对应的联系人查询出来

    */

   @Test

   public void test1(){

     

      //获取EntityManager

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      //查询客户时,延迟加载联系人

      Customer customer = em.find(Customer.class, 1L);

      System.out.println(customer.getCustName());

      Set<LinkMan> linkMans = customer.getLinkMans();

      for (LinkMan linkMan : linkMans) {

         System.out.println(linkMan);

      }

      tx.commit();

      em.close();

   }

   观察测试结果发现,查询一方延迟加载多方数据,这点和xml配置是一样的。测试结果如下:

 

      1. 查询多方,立即加载一方数据

/**

    * 查询联系人,也要把联系人所属的客户查询出来

    */

   @Test

   public void test2(){

     

      //获取EntityManager

      EntityManager em = JPAUtils.getEntityManager();

      EntityTransaction tx = em.getTransaction();

      tx.begin();

     

      //查询联系人时,会立即加载客户

      LinkMan linkMan = em.find(LinkMan.class, 1L);

      System.out.println(linkMan.getLkmName());

      Customer customer = linkMan.getCustomer();

      System.out.println(customer);

 

      tx.commit();

      em.close();

   }

 

观察测试结果发现:查询多方立即加载一方数据,这点和xml正好是相反的。我们也不需要改变这种情况,因为我们就是需要查询多方立即加载一方数据。测试结果如下:

提示:如果想改变默认的抓取策略,可以给OneToMany、ManyToOne注解加fetch属性:

fetch属性可以取如下两个值:

1、EAGER:表示立即加载;

2、LAZY:表示延迟加载;

在注解里,默认情况下,查询一方是延迟加载多方,如果想改变这种情况,可以在OneToMany中加fetch属性,如下:

@Entity

@Table(name="cst_customer")

public class Customer implements Serializable{

   private static final long serialVersionUID = 1L;

   @Id

   @Column(name="cust_id")

   @GeneratedValue(strategy=GenerationType.IDENTITY)

   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;

  

   //mappedBy:指定一方放弃外键维护权

@OneToMany(mappedBy="customer",cascade={CascadeType.PERSIST,CascadeType.REMOVE},fetch=FetchType.EAGER)

   private Set<LinkMan> linkMans = new HashSet<LinkMan>();

   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 + "]";

   }

  

  

}

修改完之后,再次运行如下代码:

/**

    * 在JPA中:查询一方延迟加载多方

    */

   @Test

   public void test1() {

      // 获取EntityManager

      EntityManager em = JPAUtils.getEntityManager();

      // 获取事务

      EntityTransaction tx = em.getTransaction();

      // 开启事务

      tx.begin();

     

      Customer customer = em.find(Customer.class, 1L);

      System.out.println(customer.getCustName());

      Set<LinkMan> linkMans = customer.getLinkMans();

      for (LinkMan linkMan : linkMans) {

         System.out.println(linkMan);

      }

      // 提交事务

      tx.commit();

      // 释放资源

      em.close();

   }

测试结果表明:查询一方会;立即加载多方数据。但是,一般也没有改变fetch的默认值。测试结果如下:

 

 

多对多:看成两个一对多的组合

转载于:https://my.oschina.net/u/3892666/blog/1835414

你可能感兴趣的文章
前端安全系列(一):如何防止XSS攻击?
查看>>
IK分词器安装
查看>>
查看Linux并发连接数
查看>>
你是谁不重要,关键是你跟谁!
查看>>
CSS中规则@media的用法
查看>>
pychecker:分析你的python代码
查看>>
我的友情链接
查看>>
DNS显性+隐性URL转发原理
查看>>
我的友情链接
查看>>
网易有道 IP地址、手机号码归属地和身份证 查询接口API
查看>>
鼠标停留在GridView某一行时行的颜色改变
查看>>
系列3:WAS Liberty Profile hello mysql jdbc
查看>>
基础知识:python模块的导入
查看>>
Android MVC之我的实现
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
关于批处理-1
查看>>
Tomcat部署Web应用方法总结
查看>>
Python3 django2.0 字段加密 解密 AES
查看>>
CCNA实验之:网络地址转换(NAT)实验
查看>>