• Hibernate结合JPA05


    一. JPA简介

    JPA是Java Persistence API的简称,中文名Java持久层Api,是JDK1.5注解或者Xml描述对象-关系表的映射关系,并将运行期的实体类对象持久化Dao数据库中!注意的是,如果两种映射发生冲突的时候XML优先于注解的方式!

    1. 什么是JPA?

    JPA由EJB 3.0软件专家组开发,作为JSR-220实现的一部分。但它又不限于EJB 3.0,你可以在Web应用、甚至桌面应用中使用。JPA的宗旨是为POJO提供持久化标准规范,由此可见,经过这几年的实践探索,能够脱离容器独立运行,方便开发和测试的理念已经深入人心了。Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。

    JPA的总体思想和现有Hibernate、TopLink、JDO等ORM框架大体一致。总的来说,JPA包括以下3方面的技术:

    ORM映射元数据

    JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;

    API

    用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。

    查询语言

    这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

    2. JPA和Hibernate的关系!

    JPA 的目标之一是制定一个可以由很多供应商实现的API,并且开发人员可以编码来实现该API,而不是使用私有供应商特有的API。因此开发人员只需使用供应商特有的API来获得JPA规范没有解决但应用程序中需要的功能。尽可能地使用JPA API,但是当需要供应商公开但是规范中没有提供的功能时,则使用供应商特有的API。

    JPA是需要Provider来实现其功能的,Hibernate就是JPA Provider中很强的一个,应该说无人能出其右。从功能上来说,JPA就是Hibernate功能的一个子集。Hibernate 从3.2开始,就开始兼容JPA。Hibernate3.2获得了Sun TCK的JPA(Java Persistence API) 兼容认证。

    只要熟悉Hibernate或者其他ORM框架,在使用JPA时会发现其实非常容易上手。例如实体对象的状态,在Hibernate有自由、持久、游离三种,JPA里有new,managed,detached,removed,明眼人一看就知道,这些状态都是一一对应的。再如flush方法,都是对应的,而其他的再如说Query query = manager.createQuery(sql),它在Hibernate里写法上是session,而在JPA中变成了manager,所以从Hibernate到JPA的代价应该是非常小的

    同样,JDO,也开始兼容JPA。在ORM的领域中,看来JPA已经是王道,规范就是规范。在各大厂商的支持下,JPA的使用开始变得广泛。

    二. JPA注解开发步骤

    1. 为实体类添加注解

    使用Hibernate的方法操作数据,我们利用xml配置文件将持久化类映射到数据库表,通过面向对象的思想,操作对象间接的操作数据库,但是xml配置写起来比较繁琐,那么我们可以使用Hibernate配置JPA注解的方式进行开发,这样简化开发步骤!

     1 @Entity
     2 @Table(name="t_customer3")
     3 public class Customer{
     4     @Id
     5     @GeneratedValue(strategy=GenerationType.IDENTITY)
     6     @Column(name="id")
     7     private Long id;
     8     
     9     @Column(name="name")
    10     private String name;
    11     
    12     @Column(name="gender")
    13     private Character gender;
    14     
    15     @Column(name="age")
    16     private Integer age;
    17     
    18     @Column(name="level")
    19     private String level;
    20 }
    注解名称注解含义
    @Entity 表明是一个JPA实体,可以使用JPA注解
    @Table table表明实体类对应一个表,name属性对应表名
    @Id 指定改变量为主键
    @GeneratedValue 主键生成策略
    @Column Column将变量对应数据库列!name指定对应列名

    补充: @Column指定实体类变量对应数据库列, name属性指定对应列名,除此之外,在介绍下@Column的其他属性

    属性名称属性介绍
    nullable boolean类型!此字段是否可以为null
    length 指定列长度
    precision 指定数字类型位数
    scale 指定小数点位数

    补充其他属性:

    临时字段

    @Transient 注解,用于给实体类添加临时属性(临时属性不需要反映到数据库中)

    1 @Transient // 临时字段,不反映到数据库中
    2 private Boolean married;
    3 public Boolean getMarried() {
    4     return married;
    5 }
    6 public void setMarried(Boolean married) {
    7     this.married = married;
    8 }

    默认值字段

    默认值字段!创建列时添加defult修饰

    1  @ColumnDefault("默认值!默认值都是字符串类型");
    2   private Integer age;

    2. 修改Hibernate的核心配置文件

    添加JPA注解的实体类,需要将类全路径配置到核心配置文件上

     <mapping class="类的全路径"></mapping>

    3. 测试

     1 public class AnnotationTest {
     2     /**
     3      * 使用注解
     4      */
     5     @Test
     6     public void testAnnotation(){
     7         Session session = HibernateUtil.openSession();
     8         Transaction tx = session.beginTransaction();
     9         
    10         Customer cust = new Customer();
    11         cust.setName("王五2222");
    12         session.save(cust);
    13         
    14         tx.commit();
    15     }
    16 }

    二. JPA的主键策略

    1. JPA的主键策略

    JPA 主键策略,没有 Hibernate 主键策略丰富,例如:

    1     @Id
    2     @GeneratedValue(strategy=GenerationType.IDENTITY)
    3     @Column(name="id")
    4     private Long id;

    其他的策略:

    • IDENTITY: 利用数据库的自增长的能力。适合 mysql

    • SEQUENCE:利用数据库的序列机制,适合 Oracle

    • TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。不同的JPA实现商生成的表名是不同的,如 OpenJPA生成openjpa_sequence_table表,Hibernate生成一个hibernate_sequences表,而TopLink则生成sequence表。这些表都具有一个序列名和对应值两个字段,如SEQ_NAME和SEQ_COUNT

    • AUTO:自动选择一个最适合底层数据库的主键生成策略。这个是默认选项,即如果只写@GeneratedValue,等价于@GeneratedValue(strategy=GenerationType.AUTO)。

    2. hibernate的主键策略

    如果使用Hibernate对JPA的实现,可以使用Hibernate对主键生成策略的扩展,通过Hibernate的@GenericGenerator实现。

    注意:id的数据类型改成String

    1 @Id
    2 //声明一个策略通用生成器,name为"system-uuid",策略strategy为"uuid"。
    3 @GenericGenerator(name="system-uuid", strategy="uuid")
    4 @Column(name="id")
    5 private String id;

    测试:

     1 /**
     2   * UUID主键策略
     3   */
     4 @Test
     5 public void testUUID(){
     6     Session session = HibernateUtil.openSession();
     7     Transaction tx = session.beginTransaction();
     8         
     9     Customer cust = new Customer();
    10     cust.setName("王五2222");
    11     cust.setId(UUID.randomUUID().toString());
    12     session.save(cust);
    13         
    14     tx.commit();
    15 }

    也可以按照如下配置,在注解中指定要使用的策略生成器,这样测试用例中就不用人工生成uuid了。

    1 @Id
    2 //声明一个策略通用生成器,name为"system-uuid",策略strategy为"uuid"。
    3 @GenericGenerator(name="system-uuid", strategy="uuid")
    4 //用generator属性指定要使用的策略生成器。
    5 @GeneratedValue(generator="system-uuid")
    6 @Column(name="id")
    7 private String id;

    三. 关联关系

    1. 一对多

    1.1 创建订单实体

     1 /**
     2  * 订单(多方)
     3  */
     4 @Entity
     5 @Table(name="t_order4")
     6 public class Order {
     7     
     8     @Id
     9     @GeneratedValue(strategy=GenerationType.IDENTITY)
    10     @Column(name="id")
    11     private Long id;
    12     
    13     @Column(name="orderno")
    14     private String orderno;
    15     
    16     //关联客户
    17     @ManyToOne
    18     @JoinColumn(name="cust_id")
    19     private Customer customer;
    20 }

    注意: @JoinColumn一般放在对应表中有外键的那个类中

    mappedBy不能同时与@JoinColumn或@JoinTable出现在同一方配置

    1.2 修改客户实体

    添加关联订单,这里使用了级联配置

    1  //关联订单
    2  @OneToMany(mappedBy="customer", cascade={CascadeType.ALL})
    3  private Set<Order> orders = new HashSet<Order>();

    CascadeType.PRESIST 级联持久化(保存)操作(持久保存拥有方实体时,也会持久保存该实体的所有相关数据。)

    CascadeType.REMOVE 级联删除操作(删除一个实体时,也会删除该实体的所有相关数据。)

    CascadeType.MERGE 级联更新(合并)操作(将分离的实体重新合并到活动的持久性上下文时,也会合并该实体的所有相关数据。)

    CascadeType.REFRESH 级联刷新操作 (只会查询获取操作)

    CascadeType.ALL 包含以上全部级联操作

    注意:以上配置需要执行相应的级联操作的特定方法,扩展阅读:http://sefcertyu.iteye.com/blog/475237

    1.3 修改Hibernate核心配置文件

    hibernate.hbm.xml中添加Order的映射

    <mapping class="com.qfedu.hibernate.pojo.Order" />

    1.4 测试

     1     /**
     2      * 测试一对多注解
     3      */
     4     @Test
     5     public void testOneToMany(){
     6         Session session = HibernateUtil.openSession();
     7         Transaction tx = session.beginTransaction();
     8         
     9         Customer cust = new Customer();
    10         cust.setName("王五3");
    11         session.save(cust);
    12     
    13         Order o1 = new Order();
    14         o1.setOrderno("201709003");
    15         
    16         cust.getOrders().add(o1);
    17         
    18         session.save(cust);
    19         //session.save(o1);
    20         
    21         tx.commit();
    22     }

    2. 多对多

    用户和角色是多对多关系!

    2.1 创建User实体

     1 /**
     2  * 用户(多方)
     3  */
     4 @Entity
     5 @Table(name="t_user1")
     6 public class User{
     7     @Id
     8     @GeneratedValue(strategy=GenerationType.IDENTITY)
     9     @Column(name="id")
    10     private Integer id;
    11     
    12     @Column(name="user_name")
    13     private String name;
    14     
    15     //关联角色
    16     @ManyToMany(cascade={CascadeType.ALL})
    17     //@JoinTable: 用于映射中间表
    18     //joinColumns: 当前方在中间表的外键字段名称
    19     //inverseJoinColumns:对方在中间表的外键字段名称
    20     @JoinTable(
    21             name="t_user_role1",
    22             joinColumns=@JoinColumn(name="user_id"),
    23             inverseJoinColumns=@JoinColumn(name="role_id"))
    24     private Set<Role> roles = new HashSet<Role>();
    25 }

    2.2 创建Role实体

     1 /**
     2  * 角色(多方)
     3  */
     4 @Entity
     5 @Table(name="t_role1")
     6 public class Role{
     7     
     8     @Id
     9     @GeneratedValue(strategy=GenerationType.IDENTITY)
    10     @Column(name="id")
    11     private Integer id;
    12     @Column(name="role_name")
    13     private String name;
    14     
    15     //关联用户
    16     @ManyToMany(mappedBy="roles")
    17     private Set<User> users = new HashSet<User>();
    18 }

    2.3 配置映射

    1   <mapping class="pojo.User" />
    2   <mapping class="pojo.Role" />

    2.4 测试

     1 /**
     2   * 测试多对多注解
     3   */
     4  @Test
     5  public void testManyToMany(){
     6      Session session = HibernateUtil.openSession();
     7      Transaction tx = session.beginTransaction();
     8         
     9       User u1 = new User();
    10       u1.setName("Helen");
    11         
    12       Role r1 = new Role();
    13       r1.setName("VIP");
    14         
    15       u1.getRoles().add(r1);
    16         
    17       session.save(u1);
    18       //session.save(r1);
    19         
    20       tx.commit();
    21 }

    3. 一对一

    3.1 创建Person实体

     1 @Entity
     2 @Table(name="t_person1")
     3 public class Person{
     4     
     5     @Id
     6     @GeneratedValue(strategy=GenerationType.IDENTITY)
     7     @Column(name="id")
     8     private Integer id;
     9     
    10     @Column(name="name")
    11     private String name;
    12     
    13     //关联身份证
    14     @OneToOne(mappedBy="person", cascade={CascadeType.ALL})
    15     private Card card;
    16 }

    3.2 创建Card实体

     1 @Entity
     2 @Table(name="t_card1")
     3 public class Card{
     4     
     5     @Id
     6     @GeneratedValue(strategy=GenerationType.IDENTITY)
     7     @Column(name="id")
     8     private Integer id;
     9     
    10     @Column(name="card_no")
    11     private String cardno;
    12     
    13     //关联公民
    14     @OneToOne
    15     @JoinColumn(name="person_id")
    16     private Person person;
    17 }

    3.3 Hibernate核心配置文件

    1    <mapping class="pojo.Person" />
    2    <mapping class="pojo.Card" />

    3.4 测试

     1  @Test
     2  public void testOneToOne(){
     3      
     4     Session session = HibernateUtil.openSession();
     5     Transaction tx = session.beginTransaction();
     6         
     7      Person p = new Person();
     8      p.setName("老王");
     9         
    10      Card c = new Card();
    11      c.setCardno("44333222");
    12         
    13      p.setCard(c);
    14         
    15      session.save(p);
    16      //session.save(c);
    17      tx.commit();
    18  }

    总结

    一对一为例:针对 mappedBy 、 @JoinColumn 、 cascade 的总结

    外键由谁来维护

    1、当关联关系的双方都不配置mappedBy 属性时,那么双方会互相生成外键,并且执行三条sql

    两条插入sql,一条额外的维护外键的sql

    2、如果有一方配置了mappedBy 属性,那么对方会生成外键

    3、mappedBy 和 @JoinColumn 不能配置在同一方中

    4、如果配置在同一方中,以mappedBy为准,@JoinColumn失效

    5、只能有一方配置 mappedBy

    级联操作

    1、在A设置了级联操作,A就应该被session操作

    2、在A方设置了级联操作,B就应该被设置为A的属性

    3、如果A中有外键,那么B应该被设置为A的属性,外键才能被填充

     

  • 相关阅读:
    Android笔记之AsyncTask
    Android笔记之使用Glide加载网络图片、下载图片
    Android笔记之OnLongClickListener
    Android笔记之ViewModel的使用示例
    分享两款好玩的单机游戏^_^
    Android笔记之Retrofit与RxJava的组合
    15张扑克,让观众心选一张,然后分成3组,每组5张。通过询问观众心选的牌是否在于某组中,最后把选中的牌找出来。
    使用Retrofit发送POST请求提交JSON数据
    Android笔记之引用aar
    不可变类
  • 原文地址:https://www.cnblogs.com/sueyyyy/p/9576325.html
Copyright © 2020-2023  润新知