一篇 JPA 总结

概述

下面是 JDBC 在 Java 应用和数据库之间的位置,充当着一个中间者,供 Java 应用程序访问所有类别的数据库,建立一个标准

JPA 如同 JDBC 一样,为 Java 应用程序使用 ORM 框架建立一个标准

  • JPA 和 Hibernate 的关系
    • JPA 是规范:JPA 本质上是一种 ORM 规范,不是 ORM 框架,只是定制了一些规范,提供了一些编程的 API 接口,具体实现由 ORM 厂商实现
    • Hibernate 是实现:Hibernate 除了是一种 ORM 框架之外,他也是一种 JPA 实现 HelloWorld
  • 步骤
    • 创建 presitence.xml,在这个文件中配置持久化单元
      • 指定跟哪个数据库进行交互
      • 指定使用哪个持久化框架以及配置该框架的基本属性
    • 创建实体类,使用 annotation 来描述实体类跟数据库表之间的映射关系
    • 使用 JPA API 完成数据的增、删、改、查操作
      • 创建 EntityManagerFactory(对应于 Hibernate 中的 SessionFactory)
      • 创建 EntityManager(对应 Hibernate 中的 Session)
  • 导入 jar 包 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency>
  • persistence.xml
    • JPA 规范要求在类路径的 META-INF 目录下防止 persistencce.xml,文件的名称是固定的 <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL"> <!--配置使用什么 ORM 产品--> <!--若 JPA 项目中只有一个 JPA 产品的实现,则可以不配置该节点--> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <class>com.jpa.first.both.many2many.Item</class> <properties> <!--配置数据库连接--> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/jpa"/> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/> <property name="hibernate.connection.username" value="root"/> <property name="hibernate.connection.password" value="zy961029"/> <!--配置 hibernate 的基本属性--> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> </properties> </persistence-unit> </persistence>

注解

  • **@Entity**
    • @Entity 标注用于实体类声明语句之前,指出该Java 类为实体类,将映射到指定的数据库表。
  • **@Table**
    • 当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用
  • **@id**
    • @Id 标注用于声明一个实体类的属性映射为数据库的主键列
    • @Id标注也可置于属性的getter方法之前
  • **@GeneratedValue**
    • @GeneratedValue 用于标注主键的生成策略,通过 strategy 属性指定。默认情况下,JPA 自动选择一个最适合底层数据库的主键生成策略:SqlServer 对应 identityMySQL 对应 auto increment
      • IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式
      • AUTO: JPA自动选择合适的策略,是默认选项
      • TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。
      • SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式
  • **@Basic**
    • 表示一个简单的属性到数据表的字段的映射,对于没有任何标注的 getXxx() 方法,默认为 @Basic
      • fetch 表示属性的读取策略,有 EAGER 和 LAZY 两种,分别为主支抓取和延迟加载
      • optional 表示该属性是否允许为 null,默认为 true
  • **@Column**
    • 当实体的属性与其映射的数据库表的列不同名时需要使用 @Column 标注说明,还有属性 unique、nullable、length
  • **@Transient**
    • 表示该属性并非一个到数据库表的字段的映射,ORM 框架将忽略该属性
    • 如果一个属性并非数据库表的字段映射,就务必将其标识为 @Transient,否则ORM 框架默认为其注解 @Basic,例如工具方法不需要映射
  • **@Temporal**
    • 在 JavaAPI 中没有定义 Date 类型的精度,而在数据库中表示 Date 类型的数据类型有 Date,Time,TimeStamp 三种精度(日期,时间,两者兼具),进行属性映射的时候可以使用 @Temporal 注解调整精度

JPA API

  • EntityManagerFactory
    • EntityManagerFactory 用来创建 EntityManager 实例
    • 使用 Persistence 类获取 EntityManagerFactory 实例,该类包含一个名为 createEntityManagerFactory 的静态方法
    • createEntityManager 有两个重载方法,如下:
    • 第二个重载方法和上述的方法唯一不同的是不需要传入第二个参数
    • isOpen(),检查 EntityManagerFactory 是否处于打开状态
    • close(),关闭 EntityManagerFactory,EntityManagerFactory 关闭后将释放所有资源,isOpen() 方法将返回 false
  • EntityManager
    • 代码 public void testJpa() { // 创建 EntityManagerFactory String persistenceUnitName = "persistenceUnit"; EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName); // 创建 EntityManager EntityManager entityManager = entityManagerFactory.createEntityManager(); // 获取事务对象并开启事务 EntityTransaction entityTransaction = entityManager.getTransaction(); entityTransaction.begin(); // 进行持久化操作 entityManager.xxx(); // 提交事务 entityTransaction.commit(); // 关闭 EntityManager entityManager.close(); // 关闭 EntityManagerFactory entityManagerFactory.close(); }
    • 常用方法测试
      • 我们在上述代码持久化部分测试以下方法,将初始化部分代码放入 **@Before** 内,将提交事务部分代码放入 **@After** 内,持久化操作方法在 **@Test** 内执行 public class EntityManagerMethodTest { private EntityManagerFactory entityManagerFactory; private EntityManager entityManager; private EntityTransaction entityTransaction; @Before public void init() { entityManagerFactory = Persistence.createEntityManagerFactory("persistenceUnit"); entityManager = entityManagerFactory.createEntityManager(); entityTransaction = entityManager.getTransaction(); entityTransaction.begin(); } @After public void destroy() { entityTransaction.commit(); entityManager.close(); entityManagerFactory.close(); } @Test public void test() { // 持久化操作 } }
      • find():类似于 Hibernate 中 Session 的 get 方法
      • getReference():类似于 Hibernate 中 Session 的 load 方法,即在需要的时候才会去执行 SQL 语句,初始化对象,否则返回的为代理对象
      • persistence():类似于 Hibernate 中 Session 的 save 方法,但此方法所要存取的对象若有 id,那么会抛异常
      • remove():类似于 Hibernate 中 Session 的 delete 方法,但此方法只可删除持久化对象,而 hibernate 的方法可以删除游离对象(不在缓存中,但在数据库中可能有对象,该对象有 id;缓存是指利用方法从数据库中获取到对象且将其初始化了,那么关闭 entityManager、提交事务后该对象依旧可使用)

关联关系映射(使用 IDEA 可以使用实体生成表,也可以使用对应的额表逆向生成实体类)

  • 单向多对一(orders - customer)
    • 表结构(oreders 表中有 customer 表的外键映射 cus_id)
    • 实体映射

      @ToString @Entity @Table(name = "customer", schema = "jpa") public class CustomerEntity { private int id; private String cusName; @Id @Column(name = "id", nullable = false) @GeneratedValue(strategy = GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "cus_name", nullable = true, length = 20) public String getCusName() { return cusName; } public void setCusName(String cusName) { this.cusName = cusName; } }

    • IDEA 逆向生成实体记得添加主键生成策略
    • 多对一映射方法测试
      • 添加数据 /** * n-1 将数据插入表中,建议先插入一的一端 */ @Test public void testMany2OnePersistence() { CustomerEntity customerEntity = new CustomerEntity(); customerEntity.setCusName("bgZyy"); OrdersEntity orderEntity1 = new OrdersEntity(); orderEntity1.setOrderNaem("CC-a-AA"); OrdersEntity orderEntity2 = new OrdersEntity(); orderEntity2.setOrderNaem("DD-b-BB"); orderEntity1.setCustomerByCusId(customerEntity); orderEntity2.setCustomerByCusId(customerEntity); entityManager.persist(customerEntity); entityManager.persist(orderEntity1); entityManager.persist(orderEntity2); }
      • 获取数据
  • 单向一对多(company - employee)
    • 表结构
    • 实体映射
    • 关联关系维护
    • 一对多方法测试
      • 添加数据 @Test public void testOne2ManyPersistence() { CompanyEntity companyEntity = new CompanyEntity(); companyEntity.setComName("IT"); EmployeeEntity employeesEntity = new EmployeeEntity(); employeesEntity.setEmpName("gg"); EmployeeEntity employeesEntity1 = new EmployeeEntity(); employeesEntity1.setEmpName("yy"); // 员工集合 Collection<EmployeeEntity> employeeEntities = new ArrayList<>(); employeeEntities.add(employeesEntity); employeeEntities.add(employeesEntity1); // 为 company 添加员工的集合信息 companyEntity.setEmployeesById(employeeEntities); // 执行保存操作 entityManager.persist(employeesEntity); entityManager.persist(employeesEntity1); entityManager.persist(companyEntity); }
      • 获取数据 @Test public void testOne2ManyFind() { CompanyEntity companyEntity; companyEntity = entityManager.find(CompanyEntity.class, 1); System.out.println(companyEntity.getComName()); System.out.println(companyEntity.getEmployeesById().size()); }
  • 双向一对一映射
    • 表结构
    • 实体映射
    • 方法测试
      • 保存数据(先保存不维护关联关系的一端,否则会多出 UPDATE 语句)
    • 使用 IDEA 反向生成实体(双向一对一
  • 双向多对多映射
    • 配置一览图(实体生成数据表),核心配置如下图所示,对于添加数据获取数据代码不再展示

JPQL(Java Persistence Query Language)

  • JPQL 语言可以是 select、update、delete 语句,他们都是通过 Query 接口封装执行的。
  • Query接口封装了执行数据库查询的相关方法。调用 EntityManager 的 createQuery、create NamedQuery 及 createNativeQuery 方法可以获得查询对象,进而可调用 Query 接口的相关方法来执行查询操作。
  • 方法测试
    • 获取某一范围所有属性的集合
    • 获取某一范围部分属性的集合,其和获取所有属性的集合所使用的方法一样,不同的是 jpql 语句不一样,且需要对应的实体有部分属性的构造器
    • 使用本地 SQL 语句查询,和以上两个所使用的方法不一样,此时使用 createNativeQuery()

JPQL 还支持二级缓存,order by 子句,group by 子句,聚合查询,having 子句,关联查询,子查询等,JPQL 还有大量函数,如字符串处理函数,算术函数和日期函数等功能,这里就不再一一列举,下面列出常用的方法和函数(了解即可):

  • 常用函数
    • concat(String s1, String s2):字符串合并/连接函数。
    • substring(String s, int start, int length):取字串函数。
    • trim([leading|trailing|both,] [char c,] String s):从字符串中去掉首/尾指定的字符或空格。
    • lower(String s):将字符串转换成小写形式。
    • upper(String s):将字符串转换成大写形式。
    • length(String s):求字符串的长度。
  • Query 接口主要方法
    • int executeUpdate(),用于执行updatedelete语句。
    • List getResultList(),用于执行select语句并返回结果集实体列表。
    • Object getSingleResult(),用于执行只返回单个结果实体的select语句
    • Query setFirstResult(int startPosition),用于设置从哪个实体记录开始返回查询结果。
    • Query setMaxResults(int maxResult),用于设置返回结果实体的最大数。与setFirstResult结合使用可实现分页查询。

Spring 整合 JPA

  • 整合什么
    • Spring 管理 EntityManager,JPA 使用声明式事务
  • 使用什么整合
    • LocalContainerEntityManagerFactoryBean,其适用于所有环境
  • 整合步骤
    • jar 包
      • Spring + Hibernate + JPA + C3P0 + MySQL
    • 创建 Spring 配置文件
      • 配置数据源
      • 配置 EntityManagerFactoryBean,即 LocalContainerEntityManagerFactoryBean,其需要属性 DataSourcejpaVendorAdapter(JPA 提供商的适配器,通过内部 bean 的方式)、packagesToScan(Entity 在哪个包下),配置 JPA 基本属性(show_sql 等)
      • 配置 JPA 使用的事务管理器(JPAtransactionManager)
      • 配置事务
    • 在 DAO 中使用 EntityManager
      • 如何获取到和当前事务关联的 EntityManager 对象?
      • 通过 **@PesistenceContext** 注解标记成员变量
    • 一览图

以上就是我所学到有关 JPA 的知识,还望有用!再就是希望大牛们可以提点建设性的建议,共同进步,先谢谢了!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Struts2 总结之Action 类访问 WEB 资源

    bgZyy
  • Java 基础 -- 泛型、集合、IO、反射

    bgZyy
  • WEB 小案例 -- 网上书城(四)

    bgZyy
  • 原来Python自带了数据库,用起来真方便!

    Python作为数据科学主流语言,被广泛用于数据读存、处理、分析、建模,可以说是无所不能。

    朱小五
  • 字典禁忌:UPDATE GLOBAL_NAME为空之后的恢复

    编辑手记:最近一个朋友遭遇到了这个问题,当GLOBAL_NAME被更新为空值之后,数据库无法启动,我们重温一下老熊的这个测试,记住结论,无论如何不要Update...

    数据和云
  • 区块链技术是目前数字版权保护最好的手段

    现今随着数字产业的快速发展,在数字版权保护方面也受到大众的关注,数字版权保护技术也成为了数字内容交易和传播的重要途径。传统的版权保护很少涉及到技术方面,而数字版...

    墨者安全科技
  • 学习SQL【1】-搭建SQL的学习环境

    最近我在学习SQL,现在就开始记录我的学习过程吧,加油!如果你也想学SQL,希望我的学习记录能对你有所帮助。 PostgreSQL是对象关系型数据库管理系统...

    爱吃西瓜的番茄酱
  • 编写高效的Android代码

    毫无疑问,基于Android平台的设备一定是嵌入式设备。现代的手持设备不仅仅是一部电话那么简单,它还是一个小型的手持电脑,但是,即使是最快的最高端的手持设备也远...

    飞雪无情
  • 【粤嵌实训】Python小游戏开发之“代码大战”

    自从 PHP 大张旗鼓宣称其为世界上最好的编程语言后,世界各路编程语言群起讨伐,战火一直蔓延到21世纪中叶。战争有时候并不总是带来灾难的,甚至还会促进世界大融...

    悠风
  • [C#]获得WindowsForm上所有特定类型的控件

    本文为原创文章、源代码为原创代码,如转载/复制,请在网页/代码处明显位置标明原文名称、作者及网址,谢谢! 开发工具:VS2017 语言:C# DotNet版本:...

    CNXY

扫码关注云+社区

领取腾讯云代金券