写点什么

Spring Data 开发手册|Java 持久化 API(JPA)需要了解到什么程度呢?

作者:浅羽技术
  • 2023-04-17
    四川
  • 本文字数:6859 字

    阅读完需:约 23 分钟

JPA,Java Persistence API 是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象/关联映射工具来管理 Java 应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合 ORM 技术

ORM:通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。本质就是将数据从一种形式转换到另外一种形式。

同时也结束了 Hibernate、TopLink 等 ORM 框架各自为营的局面。JPA 充分吸收了 Hibernate、TopLink 等 ORM 框架的基础上发展起来的,使用方便,伸缩性强

注意: JPA 不是一种新的 ORM 框架,它的出现只是用于规范现有的 ORM 技术,它不能取代现有的 Hibernate 等 ORM 框架,相反,采用 JPA 开发时,我们仍将使用这些 ORM 框架,只是此时开发出来的应用不在依赖于某个持久化提供商。应用可以在不修改代码的情况下载任何 JPA 环境下运行,真正做到低耦合,可扩展的程序设计。类似于 JDBC,在 JDBC 出现以前,我们的程序针对特性的数据库 API 进行编程,但是现在我们只需要针对 JDBC API 编程,这样能够在不改变代码的情况下就能换成其他的数据库。

JPA 是一套规范,不是一套产品。Hibernate 是一套产品,如果这些产品实现了 JPA 规范,那么我们可以叫它们为 JPA 的实现产品。使用 JPA,就可以把我们的应用从 Hibernate 中解脱出来,那么现在问题来了::如何使用 JPA 来开发呢?

准备好了吗,进入正题,起飞!

首先,先带大家看一下本篇文章的大致介绍。

没目录怎么知道这篇到底有多少干货呢?

  • 以前的开发模式

  • JPA 是什么

  • JPA 解决了什么问题

  • JPA 的第一个 HelloWord 程序

  • 详解配置文件

  • 常用的注解

  • 一对一的问题

  • 一对多的问题

  • 多对多的问题

  • JPA 中常见的方法

  • JPA 中对象的状态

  • 注意事项

是不是很清晰呢,什么?还不进入正文,来了,安排上,一个一个来:

回顾以前的开发模式

以前开发的时候我们的 DAO 层,要么使用 Hibernate、要么使用 iBatis、dbutils、toplink


需求:假设现在的产品的 1.0 版本的 DAO 的实现使用的是 Hibernate、现在老板要求将 DAO 层换成 TopLink


按照现在的解决方案整个 DAO 层都是需要重写的,很耗费人力和物力,增加了成本

有没有一种方案?这种方案就是如果我们需要换 ORM 框架,我们的整个 DAO 层都不需要改变只是需要改变配置文件就可以了呢?

JPA 技术技术因此而生

JPA 是什么

JPA 实际上是 sun 公司出的一套规范、这套规范的作用是为了解决市场上 ORM 框架一家独大的问题


JPA 是一套规范,只要我们的 ORM 框架实现了这套规范,那么在使用这个 ORM 框架的时候,就不需要面对于某一种 ORM 产品的 API 来进行编程,而是统一的面向于 JPA 来进行编程,这个时候即使你的 ORM 产品改变了,那么你的 DAO 层面向于 JPA 编程的代码是不用变的


JPA 解决了什么问题

JPA 统一了 ORM 框架访问数据库的 API

JPA 解决了 ORM 框架一家独大的问题

JPA 的第一个 HelloWorld 程序

导包


编写配置文件

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="hibernateJPA" transaction-type="RESOURCE_LOCAL">  <properties>   <property name="hibernate.hbm2ddl.auto" value="update"></property>   <property name="hibernate.show_sql" value="true"></property>   <property name="hibernate.format_sql" value="true"></property>   <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"></property>   <property name="hibernate.connection.url" value="jdbc:mysql:///qianyu"></property>   <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"></property>   <property name="hibernate.connection.username" value="root"></property>   <property name="hibernate.connection.password" value="root"></property>  </properties> </persistence-unit> </persistence>  

复制代码


编写 Java 实体和注解

@Table(name="t_user")     //设置当前的类的对象对应的表名字@Entity                   //表示当前的这个类是一个持久化的实体public class User { @Id                  //这个表示的是当前的字段是主键 @GeneratedValue(strategy=GenerationType.IDENTITY)   //这个表示的是主键的生成策略(自增长) @Column(name="uId") private int uId;  @Column(name="userName")   //列的名字 private String userName;  @Column(name="password") private String password;
}
复制代码


测试

@Test public void testHelloWorld() throws Exception {    //第一步:创建实体管理的工厂  EntityManagerFactory ef=Persistence.createEntityManagerFactory("hibernateJPA");  //通过工厂创建实体的管理器  EntityManager em=ef.createEntityManager();  //第三步:开启事务  em.getTransaction().begin();  //操作业务逻辑    User user=new User();  user.setUserName("浅羽");  user.setPassword("123");  //保存用户实体到数据库  em.persist(user);    //提交事务  em.getTransaction().commit();  //关闭管理器  em.close();  ef.close(); }
复制代码


点击并拖拽以移动

详解配置文件

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1">    <!--         persistence-unit:这个叫做持久化的单元   这个的作用就是配置 访问数据库的信息         name:逻辑意义上可以随便写  但是一般情况下见名之意  这个一般情况下写JPA的实现产品的名字         transaction-type:事务的类型            RESOURCE_LOCAL:局部事务                                事务的分类                            全局事务                               举例:张三给李四转账(建设银行----中国银行)                                                            局部事务                               举例:所有的操作在同一个库里头执行                                                            粗粒度事务                              举例:能够对整个类或者方法体进行事务的管理                                                细粒度事务                                  举例:就是能够对某几行代码进行事务的管理                                      --> <persistence-unit name="hibernateJPA" transaction-type="RESOURCE_LOCAL">  <properties>   <property name="hibernate.hbm2ddl.auto" value="update"></property>   <property name="hibernate.show_sql" value="true"></property>   <property name="hibernate.format_sql" value="true"></property>   <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"></property>   <property name="hibernate.connection.url" value="jdbc:mysql:///qianyu"></property>   <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"></property>   <property name="hibernate.connection.username" value="root"></property>   <property name="hibernate.connection.password" value="root"></property>  </properties> </persistence-unit> </persistence>  
复制代码


点击并拖拽以移动

常用的注解线程池技术

@Table:表示的是当前的实体对应的数据库中的表名字@Entity:表示的是当前的实体是一个持久化的实体@Id:这个表示当前的属性是一个主键@GeneratedValue:主键的生成策略strategy=GenerationType.IDENTITY:这个表示的是主键自增长strategy=GenerationType.AUTO:使用表来生成目标表的主键strategy=GenerationType.SEQUENCE:使用序列来生成主键@Column:jAVA的属性对应的数据库表的列的名字Name:名字Length:表示的是字段的长度nullable=false:这个表示的是不能为nullunique=true:是否是唯一的@Transient :当前字段在数据库中不对应列@Enumerated:表示的是枚举在数据库中的映射使用下标还是字符串EnumType.STRING:表示的是以字符串的形式显示EnumType.ORDINAL:表示枚举在数据中以下标的形式显示@Lob:修饰String类型的时候 表示的大文本       修饰byte[]的时候表示存储的是二进制
复制代码


点击并拖拽以移动

一对一的问题

需求:一个人对应了一个身份证、一个身份证也唯一对应了一个人

  • 身份证----->人

  • 一对一的关系

代码演示:

声明IdCard类

@Entity@Tablepublic class IdCard {  @Id private String cardNum;   private Date startTime;  private Date endTime;  //一个身份证唯一的对应了一个人 @OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY) @JoinColumn(name="pId")     //这个表示的是添加一个列 这个列映射下面对象中的这个Id private People people;}
复制代码


点击并拖拽以移动

声明People类

@Entity@Tablepublic class People { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int pId;  private String pName;  private String pTel;  //一个人对应了一个身份证 //在关联关系中 配置了mappedBy的哪一方没有权限维护另外一方 //mappedBy的值就是当前的类在下面对象中声明的这个名字 @OneToOne(mappedBy="people",cascade=CascadeType.ALL) private IdCard idCard;}
复制代码


点击并拖拽以移动

测试

@Test public void testHelloWorld() throws Exception {  EntityManager entityManager=JPAUtils.getEntityManager();    IdCard idCard=new IdCard();  idCard.setCardNum("510...x");  idCard.setStartTime(new Date());  idCard.setEndTime(new Date());    People people=new People();  people.setpName("小羽");  people.setpTel("1234566");    idCard.setPeople(people);    entityManager.persist(idCard);     JPAUtils.close(); } 
复制代码


点击并拖拽以移动

一对多的问题

需求:部门和员工的对应

  • 部门----->员工

  • 一对多的关联关系

代码演示:

声明部门对象

@Entity@Tablepublic class Dept { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int dId;  private String dName;  private String dDes;  //一个部门有多个员工 @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="dept") private Set<Employee> emps;}
 
复制代码


点击并拖拽以移动

声明员工对象

@Entity@Tablepublic class Employee {
 @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private int empId;  private String empName;  @ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY) @JoinColumn(name="dId") private Dept dept;}
复制代码


点击并拖拽以移动

测试

@Test public void testOne2Many() throws Exception {  EntityManager entityManager=JPAUtils.getEntityManager();  Employee emp=new Employee();  emp.setEmpName("小娜");      Dept dept=new Dept();  dept.setdName("研发部");  dept.setdDes("专门搞开发的");    emp.setDept(dept);  entityManager.persist(emp);    JPAUtils.close(); }
复制代码


点击并拖拽以移动

多对多的问题

需求:一个学生可以被多个老师教,一个老师也可以教多个学生

  • 学生----->老师            一对多

  • 老师----->学生            一对多

  • 老师和学生的最终关系    多对多的关联关系

代码演示:

编写老师实体

@Entity@Tablepublic class Teacher {  @Id private String tNum;  private String tName; @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY) @JoinTable(name="t_teacher_student", joinColumns=@JoinColumn(name="tNum"),          //映射的是当前这个类的主键 inverseJoinColumns={@JoinColumn(name="stuNum")})     //映射的是对方表的主键 private Set<Student> students;}
复制代码


点击并拖拽以移动

编写学生实体

@Entity @Tablepublic class Student {  @Id private int stuNum;  private String stuName;  private int age;  @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="students") private Set<Teacher> teachers;}
复制代码


点击并拖拽以移动

测试

@Test public void testMany2Many() throws Exception {     EntityManager em=JPAUtils.getEntityManager();    Teacher teacher=new Teacher();  teacher.settName("小羽");  teacher.settNum("007");    Set<Student> students=new HashSet<Student>();    Student student=new Student();  student.setAge(18);  student.setStuName("小白");  student.setStuNum(100);    Student student1=new Student();  student1.setAge(19);  student1.setStuName("小娜");  student1.setStuNum(1000);    Student student2=new Student();  student2.setAge(20);  student2.setStuName("小黑");  student2.setStuNum(10000);      students.add(student);  students.add(student1);  students.add(student2);    teacher.setStudents(students);    em.persist(teacher);      JPAUtils.close(); }
复制代码


点击并拖拽以移动

JPA 中常见的方法

代码演示:

常见方法

public void testMethod() throws Exception {  EntityManager entityManager=JPAUtils.getEntityManager();  User user= new User();  user.setUserName("小灰");  user.setuId(1);    //添加数据的方法//  entityManager.persist(user);    //查询数据  //User user2=entityManager.find(User.class,1);    //这个写的是HQL语句//  Query query=entityManager.createQuery("from User");//  List list=query.getResultList();    //下面这个方法有主键值 那么就修改  没有主键值 就插入  //entityManager.merge(user);    /*创建的是本地SQL的查询  Query query=entityManager.createNativeQuery("select * from user");  List list=query.getResultList();*/    //一般用在查询中 获取最新的这个数据//  entityManager.refresh(user);    User user2=entityManager.find(User.class,1);    entityManager.remove(user2);  //System.out.println(list);    JPAUtils.close(); } 
复制代码


点击并拖拽以移动

JPA 中对象的状态

对象的状态:

  • 新建状态: User user = new User();和数据库以及内存没有任何关联,对象仅仅是被 new 出来之后的这种状态

  • 托管状态: 对象调用了 find persist refresh merge 或者查询之后的这个对象状态就叫做托管状态,托管状态的数据是被 entityManager 管理的,并且内存和数据库的数据是对应了,这个时候如果你改变了内存的这个数据的话,并且进行提交的话,那么这个数据会和数据库进行同步

  • 游离状态: 当前的对象调用了 clear 方法之后在 close 方法之前的这段时间,这个对象处于游离状态。clear:表示的是清楚内存和数据库数据的对应的关系

  • 删除状态: 当前对象 close 之后的对象的这种状态,就称为删除状态

 

注意事项

表名不写默认就是类作为表名

column 不写,表的列名就是类的属性名

@GeneratedValue 后面值不写默认是 auto

结语

JPA 是我们开发中离不开的经常用到的技术,其涉及的技术和知识面其实远不止上面列出的这些。

后续浅羽会继续更新关于 JPA 的开发知识,只希望能对大家有所帮助,谢谢大家的支持!



发布于: 2023-04-17阅读数: 28
用户头像

浅羽技术

关注

才疏学浅,习习而为,编程羽录,与你同行。 2019-02-26 加入

分享一些计算机信息知识、理论技术、工具资源、软件介绍、后端开发、面试、工作感想以及生活随想等一系列文章。

评论

发布
暂无评论
Spring Data开发手册|Java持久化API(JPA)需要了解到什么程度呢?_Java_浅羽技术_InfoQ写作社区