zoukankan      html  css  js  c++  java
  • JPA---Java持久层API

    JPA讲义

    Java持久层API

     

    目  录

    1      JPA概述.................................................................................................................. 3

    1.1      JPA是什么................................................................................................... 3

    1.1.1     ORM是什么...................................................................................... 3

    1.1.2     标准是什么....................................................................................... 3

    1.2      JPA的实现者............................................................................................... 4

    1.3      JPA的作用是什么....................................................................................... 4

    2      入门示例................................................................................................................ 4

    2.1      配置流程图.................................................................................................. 5

    2.2      配置步骤说明.............................................................................................. 5

    2.3      配置步骤...................................................................................................... 5

    2.3.1     第一步:创建Maven项目.............................................................. 6

    2.3.2     第二步:创建一个总配置文件....................................................... 7

    2.3.3     第三步:封装JPAUtils工具类......................................................... 8

    2.3.4     第四步:创建映射实体类............................................................... 8

    2.3.5     第五步:在总配置文件中加载映射实体类................................... 9

    2.3.6     第六步:操作实体类保存数据..................................................... 10

    3      移植Hibernate JPA代码到OpenJPA(了解)................................................... 11

    3.1      说明............................................................................................................ 11

    3.2      示例代码.................................................................................................... 12

    3.2.1     第一步:导入OpenJPA依赖......................................................... 12

    3.2.2     第二步:修改总配置文件............................................................. 12

    3.3      使用JPA的好处........................................................................................ 13

    4      JPA实现CRUD..................................................................................................... 13

    5      JPA常用 API说明............................................................................................... 15

    5.1      映射注解说明............................................................................................ 15

    5.2      JPA常用API说明...................................................................................... 15

    6      JPA多表关联查询................................................................................................ 16

    6.1      一对多实现................................................................................................ 16

    6.1.1     说明................................................................................................. 16

    6.1.2     配置步骤......................................................................................... 17

    6.2      多对一实现................................................................................................ 20

    6.2.1     说明................................................................................................. 20

    6.2.2     配置步骤......................................................................................... 20

    6.3      双向一对多|多对一(了解).................................................................. 22

    6.3.1     说明................................................................................................. 22

    6.3.2     配置步骤......................................................................................... 23

    6.4      一对一实现................................................................................................ 24

    6.4.1     说明................................................................................................. 24

    6.4.2     配置步骤......................................................................................... 24

    6.5      配置多对多................................................................................................ 28

    6.5.1     说明................................................................................................. 28

    6.5.2     配置步骤......................................................................................... 28

    7      JPA逆向工程........................................................................................................ 33

    7.1      说明............................................................................................................ 33

    7.2      步骤............................................................................................................ 34

    7.2.1     第一步:创建JPA项目.................................................................. 34

    7.2.2     第二步:生成JPA代码.................................................................. 35

    7.2.3     第三步:导入所需jar依赖........................................................... 43

    7.2.4     第四步:更新项目......................................................................... 44

    8      JPQL语言............................................................................................................. 44

    8.1      说明............................................................................................................ 44

    8.2      示例代码.................................................................................................... 44

    8.3      JPQL补充:N+1问题............................................................................... 48

     

    1           JPA概述

    1.1        JPA是什么

    JPA (Java Persistence API) Java持久化API。是一套Sun公司 Java官方制定的ORM 方案,是规范,是标准 ,sun公司自己并没有实现

     

    关注点: ORM  ,标准 概念 (关键字)

     

    1.1.1        ORM是什么

    ORM(Object Relational Mapping) 对象关系映射。

    问:ORM有什么用?

    在操作数据库之前,先把数据表与实体类关联起来。 然后通过实体类的对象操作(增删改查)数据库表,这个就是ORM的行为!

    所以:ORM是一个实现使用对象操作数据库的设计思想!!!

    通过这句话,我们知道JPA的作用就是通过对象操作数据库的,不用编写sql语句。

    1.2        JPA的实现者

    既然我们说JPA是一套标准,意味着,它只是一套实现ORM理论的接口。没有实现的代码。

    那么我们必须要有具体的实现者才可以完成ORM操作功能的实现!

      

    市场上的主流的JPA框架 (实现者)有:

    Hibernate (JBoos)、EclipseTop(Eclipse社区)、OpenJPA (Apache基金会)。

      

    其中Hibernate是众多实现者之中,性能最好的。所以,我们本次教学也是选用Hibernate框架作为JPA的主讲框架。

    提醒: 学习一个JPA框架,其他的框架都是一样使用

    1.3        JPA的作用是什么(问题)

    JPA是ORM的一套标准,既然JPA为ORM而生,那么JPA的作用就是实现使用对象操作数据库,不用写SQL!!!.

    问题:数据库是用sql操作的,那用对象操作,由谁来产生SQL?

    答:JPA实现框架

    2           入门示例

    任何框架的学习,都建议从配置流程图开始。所以我们来一起理解JPA的配置流程图。

    2.1        配置流程图

     

    1. 我们需要一个总配置文件persistence.xml存储框架需要的信息 (注意,文件名不要写错,而且必须放在classpath/META-INF文件夹里面)

    2. 我们需要一个Persistence持久类对象来读取总配置文件,创建实体管理工厂对象

    3. 我们需要实体管理工厂获得数据库的操作对象实体管理对象EntityManager。

    4. 我们通过EntityManager操作数据库之前,必须要先配置表与实体类的映射关系,从而实现使用对象操作数据库!!!

    2.2        配置步骤说明

    第一步:导入包 (不管什么框架,首先要做的事情)

    第二步:创建一个总配置文件

    第三步:创建一个JPAUtils获得操作对象EntityManager

    第四步:创建一个实体类,并且配置好映射注解

    第五步:在总配置文件加载实体类

    第六步:测试代码(需求:插入数据到用户表)

    2.3        配置步骤

     需求:编写一个JPA的项目,插入一条数据到学生信息表。

    2.3.1        第一步:创建Maven项目

    说明:我们这里是基于hibernate实现的,所以要导入Hibernate的JPA规范包

    --使用maven构建的配置--

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

      <modelVersion>4.0.0</modelVersion>

      <groupId>cn.zj</groupId>

      <artifactId>jpa-demo01-start</artifactId>

      <version>0.0.1-SNAPSHOT</version>

     

      <dependencies>

        <!-- hibernate框架实现 JPA 依赖 -->

       <dependency>

           <groupId>org.hibernate</groupId>

           <artifactId>hibernate-entitymanager</artifactId>

           <version>4.3.6.Final</version>

       </dependency>

        <!--jdbc驱动 -->

       <dependency>

          <groupId>mysql</groupId>

          <artifactId>mysql-connector-java</artifactId>

          <version>5.1.40</version>

       </dependency>

      </dependencies>

    </project>

    2.3.2        第二步:创建一个总配置文件

    注意:文件必须放在classpath:/META-INF/persistence.xml

     

    说明:Eclipse已经支持了JPA框架,所有不需要配置xsd文件,直接使用

     

    配置信息如下:

    <?xml version="1.0" encoding="UTF-8"?>

    <persistence version="2.0" 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 ">

      <persistence-unit name="mysql-jpa">

           <!-- 四要素 org.hibernate.cfg.Environment-->

           <properties>

            <!-- 如果使用Hibernate实现的JPA,使用的就是Hibernate的环境参数 -->

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

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

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

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

    <!--可选配置-->

    <!--控制台打印sql语句-->

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

            <!-- 格式化输出SQL -->

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

        </properties>

      </persistence-unit>

    </persistence>

    2.3.3        第三步:封装JPAUtils工具类

    创建一个工具类JPAUtils,获得操作对象(EntityManager)

    package cn.zj.jpa.util;

     

    import javax.persistence.EntityManager;

    import javax.persistence.EntityManagerFactory;

    import javax.persistence.Persistence;

     

    public class JPAUtils {

     

        //同一个应用中,应该保证只有一个实例工厂。

        public static EntityManagerFactory emf = createEntityManagerFactory();

       

        //1.获得实体管理工厂

        private static EntityManagerFactory createEntityManagerFactory(){

           EntityManagerFactory emf = Persistence.createEntityManagerFactory("mysql-jpa");

           return emf;

        }

     

        //2.获得实体管理类对象

        public static EntityManager getEntityManger(){

           EntityManager entityManager = emf.createEntityManager();

           return entityManager;

        }

    }

    2.3.4        第四步:创建映射实体类

    创建一个映射的实体类,将JPA的映射注解写在实体类里面。

    package cn.zj.jpa.entity;

    import javax.persistence.Column;

    import javax.persistence.Entity;

    import javax.persistence.GeneratedValue;

    import javax.persistence.GenerationType;

    import javax.persistence.Id;

    import javax.persistence.Table;

     

    //1.指定实体类与表名的关系

    //@Entity注解,指定该实体类是一个基于JPA规范的实体类

    @Entity

    //@Table注解,指定当前实体类关联的表

    @Table(name="tb_student")

    public class Student {

        //@Id注解:声明属性为一个OID属性

        @Id

        //@GeneratedValue注解,指定主键生成策略

        @GeneratedValue(strategy=GenerationType.IDENTITY)

        //@Column注解,设置属性与数据库字段的关系,如果属性名和表的字段名相同,可以不设置

        @Column(name="stu_id")

        private Long stuId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '学生编号',

        @Column(name="stu_name")

        private String stuName;//VARCHAR(50) NULL DEFAULT NULL COMMENT '学生名字',

        @Column(name="stu_age")

        private Integer stuAge;//INT(11) NULL DEFAULT NULL COMMENT '学生年龄',

        @Column(name="stu_password")

        private String stuPassword;//VARCHAR(50) NULL DEFAULT NULL COMMENT '登录密码',

      

    public Student() {

           super();

        }

    //补全get、set方法

    }

    2.3.5        第五步:在总配置文件中加载映射实体类

    <?xml version="1.0" encoding="UTF-8"?>

    <persistence version="2.0" 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 ">

      <persistence-unit name="mysql-jpa">

     

           <!-- 加载实体类

              基于hibernate框架的JPA已经实现了自动载入映射实体类,所以不配置也是可以的。建议还是加上配置。如果不写容易忽略加载的实体类有哪些

             -->

            <class>cn.zj.jpa.entity.Student</class>

           <!-- 四要素 org.hibernate.cfg.Environment-->

           <properties>

            <!-- 如果使用Hibernate实现的JPA,使用的就是Hibernate的环境参数 -->

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

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

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

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

    <!--可选配置-->

    <!--控制台打印sql语句-->

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

            <!-- 格式化输出SQL -->

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

        </properties>

      </persistence-unit>

    </persistence>

    2.3.6        第六步:操作实体类保存数据

    创建一个StudentDAOTest类,测试保存一个学生。

    package cn.zj.jpa;

     

    import javax.persistence.EntityManager;

    import javax.persistence.EntityTransaction;

    import org.junit.Test;

    import cn.zj.jpa.entity.Student;

    import cn.zj.jpa.util.JPAUtils;

     

    public class StudentDAOTest {

     

        @Test

        public void persist(){

           //1.获得实体管理类

           EntityManager manager = JPAUtils.getEntityManger();

           //2、获取事物管理器

           EntityTransaction transaction = manager.getTransaction();

           transaction.begin();

           //3、创建实体对象

           Student s=new Student();

           s.setStuName("张三");

           s.setStuAge(18);

          s.setStuPassword("zj");

           //4、保存到数据库

           manager.persist(s);

           //5、提交事物

           transaction.commit();

           //6、关闭资源

           manager.close();

        }

    }

    测试结果

     

    通过操作实体对象保存数据成功!!!

    2.4        使用JPA的好处

    使用JPA,可以直接使用对象操作数据库,由框架根据映射的关系生成SQL。不用开发人员编写。这样做,开发人员就不用编写SQL语句了

      

    问题:这样有什么好处呢?

    答:不同的数据库的SQL语法是有差异,如果不需要编写SQL语句。就屏蔽各种数据库SQL的差异。那么,编写的代码就可以一套代码兼容多种数据库!!!!

    3           JPA实现CRUD

    修改StudentDAOTest类,测试crud操作

    //通过OID删除

        @Test

        public void remove(){

           //1.获得实体管理类对象

           EntityManager entityManager = JPAUtils.getEntityManger();

           //2.打开事务

           EntityTransaction transaction = entityManager.getTransaction();

           //3.启动事务

           transaction.begin();

           //4.创建数据,删除数据必须使用持久化对象

           Student s=entityManager.find(Student.class, 2L);

          

           //5.插入

           entityManager.remove(s);;

           //6。提交

           transaction.commit();

           //7.关闭

           entityManager.close();

        }

          

          

        //更新

        @Test

        public void merge(){

           //1.获得实体管理类对象

           EntityManager entityManager = JPAUtils.getEntityManger();

           //2.打开事务

           EntityTransaction transaction = entityManager.getTransaction();

           //3.启动事务

           transaction.begin();

           //4.创建数据

           Student s=new Student();

           s.setStuName("李四");

           //更新必须要有一个OID

           s.setStuId(3L);

           //5.更新

           entityManager.merge(s);

           //6。提交

           transaction.commit();

           //7.关闭

           entityManager.close();

          

        }

          

        //通过OID获得数据

        @Test

        public void find(){

           //1.获得实体管理类对象

           EntityManager entityManager = JPAUtils.getEntityManger();

           //通过OID查询数据

           Student student = entityManager.find(Student.class, 1L);

           System.out.println(student.getStuName());

           entityManager.close();

        }

       

        //通过OID获得数据

        @Test

        public void getReference(){

           //1.获得实体管理类对象

           EntityManager entityManager = JPAUtils.getEntityManger();

           /**

            * getReference()和find()方法的区别:

            * getReference基于懒加载机制,即需要使用对象的时候,才执行查询。

            */

           Student student = entityManager.getReference(Student.class, 1L);

           System.out.println(student.getStuName());

           entityManager.close();

        }

    4           JPA常用 API说明

    4.1        映射注解说明

    注解

    说明

    @Entity

    声明该实体类是一个JPA标准的实体类

    @Table

    指定实体类关联的表,注意如果不写表名,默认使用类名对应表名。

    @Column

    指定实体类属性对应的表字段,如果属性和字段一致,可以不写

    @Id

    声明属性是一个OID,对应的一定是数据库的主键字段

    @GenerateValue

    声明属性(Object ID)的主键生成策略

    @SequenceGenerate

    使用SEQUENCE策略时,用于设置策略的参数

    @TableGenerate

    使用TABLE主键策略时,用于设置策略的参数

    @JoinTable

    关联查询时,表与表是多对多的关系时,指定多对多关联表中间表的参数。

    @JoinColumn

    关联查询时,表与表是一对一、一对多、多对一以及多对多的关系时,声明表关联的外键字段作为连接表的条件。必须配合关联表的注解一起使用 <key>

    @OneToMany

    关联表注解,表示对应的实体和本类是一对多的关系

    @ManyToOne

    关联表注解,表示对应的实体和本类是多对一的关系

    @ManyToMany

    关联表注解,表示对应的实体和本类是多对多的关系

    @OneToOne

    关联表注解,表示对应的实体和本类是一对一的关系

    4.2        JPA常用API说明

    API

    说明

    Persistence

    用于读取配置文件,获得实体管理工厂

    EntityManagerFactory

    用于管理数据库的连接,获得操作对象实体管理类

    EntityManager

    实体管理类,用于操作数据库表,操作对象

    EntityTransaction

    用于管理事务。开始,提交,回滚

    TypeQuery

    用于操作JPQL的查询的

    Query

    用于操作JPQL的查询接口,执行没有返回数据的JPQL(增删改)

    CriteriaBuilder

    用户使用标准查询接口 Criteria查询接口

    5           JPA多表关联查询

    多个关联查询作用(导航查询):就是实现使用一个实体类对象查询多个表的数据。

      配置多表联系查询必须有两个步骤;

    1)、在实体类里面建立表与表之间的关系。

    2)、在实体类配置关联关系,JPA使用注解配置

     

    多表关联的E-R图如下:

     

    根据ER图,创建数据库表!!!

    5.1        一对多实现 (单向)

    需求:通过ID查询一条学生表的记录,同时查询该学生的对应的成绩的信息!

    5.1.1        说明

    如图所示:一个学生可以有多条成绩的记录,一条成绩的记录只属于一个学生,所以学生表与成绩表的关系是一对多的关系。

     

    所以,通过JPA配置一对多的关系,可以通过学生表对应的实体类对象同时获得两个表的数据。

    5.1.2        配置步骤

    5.1.2.1    第一步:创建项目

    说明:复制入门示例的项目即可。

    5.1.2.2    第二步:创建单表实体类

    (1)创建Student类

    @Entity

    @Table(name="tb_student")

    public class Student {

        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)

        @Column(name="stu_id")

        private Long stuId;

        @Column(name="stu_name")

        private String stuName;

        @Column(name="stu_age")

        private Integer stuAge;

        @Column(name="stu_password")

        private String stuPassword;

      

    public Student() {

           super();

        }

    //补全get、set方法

    }

    (2)创建Score类

    @Entity

    @Table(name="tb_score")

    public class Score{

     

        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)

        @Column(name="sco_id")

        private Long scoId; 

        @Column(name="sco_subject")

        private String scoSubject;

        @Column(name="sco_score")

        private Float scoScore;

        @Column(name="stu_id")

        private Long stuId;

     

        public Score() {

           super();

        }

    // 补全getset方法

    }

    5.1.2.3    第三步:配置一对多关联关系

    说明:通过@OneToMany注解配置。

    修改Student类,配置一对多关系。

        /**

         * 单向一对对,应该有学生来维护关系

         *

         * 一个学生对应多个成绩,一对多关系

         * 多个成绩我们使用list封装起来

         *

         * JPA 使用 @OneToMany 映射一对多

         * fetch : 抓取策略

         *       FetchType.LAZY 懒加载,默认 (只有关联对象在用到的时候才会去发送新的sql,默认关联对象不会查询)

         *           会多生成sql语句 :N+1

         *       FetchType.EAGER 迫切查询 (多表连接查询,只会发送一条sql语句)

         * @JoinColumn 设置两张表之间外键列

         */

        @OneToMany(fetch=FetchType.EAGER)

        @JoinColumn(name="stu_id")

        private List<Score> scores;

        public void setScores(List<Score> scores) {

             this.scores = scores;

        }

    5.1.2.4    第四步:测试一对多查询

    @Test

    public void testOne2Many(){

        //1.获得实体管理类

        EntityManager manager = JPAUtils.getEntityManger();

       

        Student student = manager.find(Student.class, 1L);

        System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());

       

        List<Score> scores = student.getScores();

        for (Score score : scores) {

           System.out.println("科目:"+score.getScoSubject()+",分数:"+score.getScoScore());

        }

       

        //6、关闭资源

        manager.close();

    }

    查询结果:

     

    一对多关联查询成功!!!

    5.2        多对一实现 (单向)

    5.2.1        说明

    需求:通过ID查询一条成绩表的记录,同时查询该成绩的对应的学生的信息!

    如图所示:成绩表里面,每一条记录只能对应一个学生,但是学生编号不是唯一的。所以成绩表里面的多条数据可以对应一个学生,所以我们称多对一的关系。

     

    5.2.2        配置步骤

    5.2.2.1    第一步:创建项目

    复制一对多示例项目即可。

    5.2.2.2    第二步:创建单表实体类

    修改Student类,去掉一对多配置即可。

    5.2.2.3    第三步:配置多对一关联关系

    修改Score类,配置多对一关系

    @Entity

    @Table(name="tb_score")

    public class Score{

     

        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)

        @Column(name="sco_id")

        private Long scoId; 

     

        @Column(name="sco_subject")

        private String scoSubject;

     

        @Column(name="sco_score")

        private Float scoScore;

       

        /**

         * jpa的多对一,关联关系中指定的外键  和 关联表的属性有冲突 

         *    解决的方案:去掉关联表中外键对应的属性

         

        /*@Column(name="stu_id")

        private Long stuId;

     

        public Long getStuId() {

           return stuId;

        }

     

        public void setStuId(Long stuId) {

           this.stuId = stuId;

        }

        */

     

        /**

         * 1、分数和学生信息是多对一的关系

         * 2、只需要一个学生的实体来引用学生的信息

         */

        @ManyToOne

        @JoinColumn(name="stu_id")

        private Student student;

       

        public Student getStudent() {

           return student;

        }

        public void setStudent(Student student) {

           this.student = student;

        }

        //补全get、set方法

    }

    5.2.2.4    第四步:测试

    @Test

        public void testMany2One(){

           //1.获得实体管理类

           EntityManager manager = JPAUtils.getEntityManger();

          

           Score score = manager.find(Score.class, 1L);

           System.out.println("科目:"+score.getScoSubject()+",分数:"+score.getScoScore());

          

           Student student = score.getStudent();

           System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());

           //6、关闭资源

           manager.close();

        }

    查询结果:

     

    多对一配置成功!!!

    5.3        双向一对多|多对一(了解)

    5.3.1        说明

    (1)查询学生信息,同时查询成绩信息。

    (2)查询成绩信息,同时也可以查询学生信息。

    5.3.2        配置步骤

    在同一个项目中,在Student类和Score类中,同时配置关联关系。

    (1)在Student类中配置一对多

        @OneToMany   //声明是一对多的关系

        @JoinColumn(name="stu_id")   //指定关联表中  外键的字段

        private List<Score> scores;

       

        public List<Score> getScores() {

           return scores;

        }

     

        public void setScores(List<Score> scores) {

           this.scores = scores;

        }

    (2)在Score类中配置多对一

    /**

         * jpa的多对一,关联关系中指定的外键  和 关联表的属性有冲突 

         *    解决的方案:去掉关联表中外键对应的属性

         

        @Column(name="stu_id")

        private Long stuId;

     

        public Long getStuId() {

           return stuId;

        }

     

        public void setStuId(Long stuId) {

           this.stuId = stuId;

        }

        */

     

        /**

         * 1、分数和学生信息是多对一的关系

         * 2、只需要一个学生的实体来引用学生的信息

         */

        @ManyToOne

        @JoinColumn(name="stu_id")

        private Student student;

       

        public Student getStudent() {

           return student;

        }

     

        public void setStudent(Student student) {

           this.student = student;

        }

    5.4        一对一实现

    5.4.1        说明

    需求:通过ID查询学生的信息,通过也获得学生对应的学生身份信息。

     

    5.4.2        配置步骤

    5.4.2.1    第一步:创建项目

    复制一对多的示例项目即可。

    5.4.2.2    第二步:创建单表实体类

    (1)创建Student类

    (2)创建Identity类

    package cn.zj.jpa.entity;

     

    import javax.persistence.Column;

    import javax.persistence.Entity;

    import javax.persistence.Id;

    import javax.persistence.Table;

     

    @Entity

    @Table(name="tb_identity")

    public class Identity {

     

        @Id

    /*由于一对一,外键表的主键字段值来自于主键表,所以只能手工输入

    手工输入ID值,可以不指定主键生成策略 */

    //  @GeneratedValue  

        @Column(name="stu_id")

        private Long stuId;

       

        @Column(name="stu_identity")

        private String stuIdentity;

     

        @Column(name="stu_no")

        private String stuNo;

        public Identity() {

           super();

        }

        //  补全get、set方法

    }

    5.4.2.3    第三步:配置一对一关联关系

    说明:一对一关联关系,也是支持双向配置的。

    可以在Student类、Identity类中同时配置关联关系。

    (1)修改Student类,配置一对一关系。

    //学生表与学生身份表是一对一的关系,意味着,一个学生只能对应一条学生身份信息

    //所以使用引用

    //1.声明关系,一对一

    @OneToOne

    //2.必须要指定关联的外键

    @JoinColumn(name="stu_id")

    private Identity identity;

     

    public Identity getIdentity() {

    return identity;

    }

    public void setiIdentity(Identity identity) {

    this.identity = identity;

    }

    (2)修改Score类

    //1.声明关系,一对一

    @OneToOne

    //2.指定关联的外键

    @JoinColumn(name="stu_id")

    private Student student;

    public Student getStudent() {

    return student;

    }

    public void setStudent(Student student) {

    this.student = student;

    }

    5.4.2.4    第四步:测试

    说明:可以分别测试方向一对一关系。

    @Test

        public void testOne2One1(){

           //1.获得实体管理类

           EntityManager manager = JPAUtils.getEntityManger();

          

           Student student = manager.find(Student.class, 1L);

           System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());

           Identity identity = student.getIdentity();

           System.out.println("学生学号:"+identity.getStuNo()+",身份证号:"+identity.getStuIdentity());

          

           //6、关闭资源

           manager.close();

        }

    @Test

        public void testOne2One2(){

           //1.获得实体管理类

           EntityManager manager = JPAUtils.getEntityManger();

           Identity identity = manager.find(Identity.class, 1L);

           System.out.println("学生学号:"+identity.getStuNo()+",身份证号:"+identity.getStuIdentity());

           Student student = identity.getStudent();

           System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());

           //6、关闭资源

           manager.close();

        }

    (1)测试学生关联身份信息

    测试结果:

     

    (2)测试身份信息关联学生。

     

    一对一配置成功!!!

    5.5        配置多对多

    5.5.1        说明

    需求:

    (1)通过ID查询学生的信息,通过该学生信息也获得对应的教师信息。

    (2)通过ID查询教师的信息,通过教师信息也获得该对应的学生信息。

      

    如图所示:一个学生可以有多个教师,一个教师也可以有多个学生,所以学生和教师的关系是多对多的关系。

      

     

      

    如上图所示:

    如果要从学生表的信息获得教师表的信息。必须需要三个条件

    1. 必须需要有一个中间表

    2. 必须需要中间表对应本表的外键

    3. 必须需要中间表对应关联表的外键

    5.5.2        配置步骤

    5.5.2.1    第一步:创建项目

    复制一个示例项目即可。

    5.5.2.2    第二步:创建单表实体类

    学生实体类Student

    //1.指定实体类与表名的关系

    //@Entity注解,指定该实体类是一个基于JPA规范的实体类

    @Entity

    //@Table注解,指定当前实体类关联的表

    @Table(name="tb_student")

    public class Student {

        //@Id注解:声明属性为一个OID属性

        @Id

        //@GeneratedValue注解,指定主键生成策略

        @GeneratedValue(strategy=GenerationType.IDENTITY)

        //@Column注解,设置属性与数据库字段的关系,如果属性名和表的字段名相同,可以不设置

        @Column(name="stu_id")

        private Long stuId;//BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '学生编号',

        @Column(name="stu_name")

        private String stuName;//VARCHAR(50) NULL DEFAULT NULL COMMENT '学生名字',

        @Column(name="stu_age")

        private Integer stuAge;//INT(11) NULL DEFAULT NULL COMMENT '学生年龄',

        @Column(name="stu_password")

        private String stuPassword;//VARCHAR(50) NULL DEFAULT NULL COMMENT '登录密码',

      

    public Student() {

           super();

        }

    //补全get、set方法

    }

    教师实体类Teacher

    @Entity

    @Table(name="tb_teacher")

    public class Teacher {

     

        @Id

        @GeneratedValue(strategy=GenerationType.IDENTITY)

        @Column(name="tea_id")

        private Long teaId;

     

        @Column(name="tea_name")

        private String teaName;

     

        @Column(name="tea_password")

        private String teaPassword;

     

        public Teacher() {

           super();

        }

        //补全get、set方法

    }

    5.5.2.3    第三步:配置多对多关系

    (1)学生关联教师,修改Student类

    // 学生表和教师表是多对多的关系

    // 所以,一个学生可以有多个教师,所以需要使用集合来存储教师信息

    //1.声明关系,多对多

    @ManyToMany

    //2.设置关联的条件

    //JoinTable用于对应中间表的设置     joinColumns设置中间表与本表关联的外键    inverseJoinColumns设置中间表与关联表对应的外键

    @JoinTable(name="tb_stu_tea" ,joinColumns=@JoinColumn(name="stu_id"),inverseJoinColumns=@JoinColumn(name="tea_id"))

    private List<Teacher> teachers;

     

    public List<Teacher> getTeachers() {

        return teachers;

    }

     

    public void setTeachers(List<Teacher> teachers) {

        this.teachers = teachers;

    }

    (2)教师关联学生,修改Teacher类

    //因为教师与学生是多对多的关系,所以一个教师也可以有多个学生,需要使用集合来存储学生的数据

    //1.声明教师和学生的关系,多对多

    @ManyToMany

    //2.设置关联的条件

    @JoinTable(name="tb_stu_tea" ,joinColumns=@JoinColumn(name="tea_id"),inverseJoinColumns=@JoinColumn(name="stu_id"))

    private List<Student> students;

     

    public List<Student> getStudents() {

        return students;

    }

     

    public void setStudents(List<Student> students) {

        this.students = students;

    }  

    5.5.2.4    测试

    5.5.2.4.1    Step1:测试学生关联教师

    测试代码

    @Test

        public void testMany2Many1(){

          

           //1.获得实体管理类

           EntityManager manager = JPAUtils.getEntityManger();

          

           Student student = manager.find(Student.class, 1L);

           System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());

          

           List<Teacher> teachers = student.getTeachers();

           for (Teacher teacher : teachers) {

               System.out.println("教师id:"+teacher.getTeaId()+",教师姓名:"+teacher.getTeaName());

           }

           //2、关闭资源

           manager.close();

        }

    测试结果:

     
    5.5.2.4.2    Step2:测试教师关联学生

    测试代码

    @Test

        public void testMany2Many2(){

          

           //1.获得实体管理类

           EntityManager manager = JPAUtils.getEntityManger();

          

           Teacher teacher = manager.find(Teacher.class, 1L);

           System.out.println("教师id:"+teacher.getTeaId()+",教师姓名:"+teacher.getTeaName());

          

           List<Student> students = teacher.getStudents();

           for (Student student : students) {

               System.out.println("学生id:"+student.getStuId()+",学生姓名:"+student.getStuName());

           }

           //2、关闭资源

           manager.close();

        }

    测试结果:

     

    多对多配置成功!!!

    6           JPA逆向工程

    6.1        说明

    所谓的逆向工程就是通过数据库的结构生成代码。

    目的:提高开发的效率

     

    6.2        步骤

    6.2.1        第一步:创建JPA项目

    (1)创建项目

     

    (2)指定项目名、JPA版本

     

    (3)完成创建

     

    6.2.2        第二步:生成JPA代码

    右击项目的src文件夹,选择new --> Other.. -->JPA的JPA Entities from Tables

    6.2.2.1    Step1:创建新的数据库连接

    (1)选择新建数据库连接

     

    (2)指定数据库类型

     

    (3)新建数据库驱动

     

    (4)配置驱动信息

    驱动版本号

     

    加载驱动jar包

     

    配置jdbc四要素

     

    6.2.2.2    Step2:配置表与表直接的关联关系

    (1)配置表关联关系

    选择表,全选即可。

     

    配置Student、Score一对多

     

    依次配置其它的关联关系即可。

     

    6.2.2.3    Step3:指定生成实体类的名称及结构

    (1)修改关联对象的属性名

     

    指定实体类的生成属性

     

    指定实体类的类名

     

    依次修改其它类的属性即可。

    6.2.2.4    Step4:生成代码

     

    6.2.3        第三步:导入所需jar依赖

    说明:Maven项目是可以导入jar包到本地的。

    方法:打开DOS窗口,进入项目的pom文件所在目录,执行命令:

    mvn dependency:copy-dependencies

    前提:已经配置了Maven环境变量。

     

    6.2.4        第四步:更新项目

     

     

    7           JPQL语言

    7.1        说明

    JPQL : Java Persistence Query Language : java持久化查询语言。

    它的作用是通过类似SQL的语法去操作实体类的对象。

    语法和SQL一样的,SQL操作的数据表,JPQL操作的对象

    作用:实现个性化的查询需求

    7.2        示例代码

    package cn.zj.jpa;

     

    import java.util.List;

     

    import javax.persistence.EntityManager;

    import javax.persistence.EntityTransaction;

    import javax.persistence.Query;

    import javax.persistence.TypedQuery;

     

    import org.junit.Test;

     

    import cn.zj.jpa.entity.Student;

    import cn.zj.jpa.util.JPAUtils;

     

    public class StudentDAOTest {

     

        //1.查询所有学生的信息

        @Test

        public void findAll(){

           //1获得操作对象

           EntityManager manager = JPAUtils.getEntityManager();

           //2.获得JPQL查询对象

           //标准的JPQL是必须要使用select

           //select语法: select 别名  from 类名 别名

           TypedQuery<Student> query = manager.createQuery("select s from Student s", Student.class);

           //返回多条查询的数据,getResultList

           //TypedQuery解决了HIbernate返回有警告的问题

           List<Student> students = query.getResultList();

           for (Student student : students) {

           System.out.println("学生名:"+student.getStuName());

           }

           manager.close();

        }

       

        //2.条件查询

        //需求:查询名字有张字的学生

        //注意:JPQL的语法,使用?设置参数,必须要在?后面设置下标值,下标值不能为负数

        @Test

        public void findByCondition(){

           //1获得操作对象

           EntityManager manager = JPAUtils.getEntityManager();

           //2.获得JPQL查询对象

           //标准的JPQL是必须要使用select

           TypedQuery<Student> query = manager.createQuery("select s from Student s where s.stuName like ?1", Student.class);

           //3.设置条件

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

           List<Student> students = query.getResultList();

           for (Student student : students) {

           System.out.println("学生名:"+student.getStuName());

           }

           manager.close();

        }

       

        @Test

        public void findByCondition1(){

           //1获得操作对象

           EntityManager manager = JPAUtils.getEntityManager();

           //2.获得JPQL查询对象

           //标准的JPQL是必须要使用select

           TypedQuery<Student> query = manager.createQuery("select s from Student s where s.stuName like :stuName", Student.class);

           //3.设置条件

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

           List<Student> students = query.getResultList();

           for (Student student : students) {

           System.out.println("学生名:"+student.getStuName());

           }

           manager.close();

        }

       

        //需求:返回学生表的记录数

        @Test

        public void count(){

           //1获得操作对象

           EntityManager manager = JPAUtils.getEntityManager();

           //2.获得JPQL查询对象

           //标准的JPQL是必须要使用select

           //JPQL中的count操作返回值是Long值,所以用Long类型接收

           TypedQuery<Long> query = manager.createQuery("select count(s) from Student s", Long.class);

           //如果返回的是一个值的查询,使用getSingleResult

           Long count = query.getSingleResult();

           System.out.println(count);

           manager.close();

        }

       

        //需求:第二页,每页三条

        @Test

        public void findByPage(){

           //1获得操作对象

           EntityManager manager = JPAUtils.getEntityManager();

           //2.获得JPQL查询对象

           //标准的JPQL是必须要使用select

           TypedQuery<Student> query = manager.createQuery("select s from Student s", Student.class);

           //设置分页条件

           //1.设置开始位置,下标从0开始,第四条数据的下标为3

           query.setFirstResult(3);

           //2.设置每页的记录

           query.setMaxResults(3);

           List<Student> students = query.getResultList();

           for (Student student : students) {

           System.out.println("学生名:"+student.getStuName());

           }

           manager.close();

        }

       

        /*

         * 命名查询语句的调用

         *

         * 所谓的命名查询,就是在实体类对象使用一个名字声明一条JPQL语句

         * 这样可以通过name值获得Query的语句

         *

         * 命名查询,在类名上做如下声明:

                     

            @NamedQuery(name="Student.findAll", query="SELECT s FROM Student s")

            public class Student {

     

         */

        @Test

        public void findAllByNamedQuery(){

           //1获得操作对象

           EntityManager manager = JPAUtils.getEntityManager();

           //2.获得一个查询命名查询语句的对象

           //可以通过该对象调用实体类声明的命名查询语句

           TypedQuery<Student> query = manager.createNamedQuery("Student.findAll", Student.class);

           List<Student> students = query.getResultList();

           for (Student student : students) {

               System.out.println(student.getStuName());

           }

           manager.close();

        }

       

        //需求:通过JQOL删除有张字的学生

        @Test

        public void removeByCondition(){

           //1获得操作对象

           EntityManager manager = JPAUtils.getEntityManager();

           EntityTransaction transaction = manager.getTransaction();

           transaction.begin();

           try {

               //2.获得JPQL查询对象

               //注意,调用操作的JPQL是不需要指定返回的类型

               Query query = manager.createQuery("delete from Student s where s.stuName like ?1");

               //参数对应?设置的下标值

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

               int count = query.executeUpdate();

               System.out.println(count);

               transaction.commit();

               manager.close();

           } catch (Exception e) {

               transaction.rollback();

               e.printStackTrace();

           }

        }

    }

    7.3        JPQL补充:N+1问题

    在一对多或者多对多查询过程中,首先查询1这一方的数据,然后根据1这一份的数据,查询多的一方的数据。

    当学生有N个的时候,总共查询次数N+1次。

    这个就称之为N+1问题

    当我们数据库的量不大的时候,N+1问题基本没有什么影响。如果当数据量很大的时候,查询数据库的次数,就很大了,这个就会影响数据库的性能。

    如何解决这个问题?

    可以通过JPQL来解决。

    @Test

        public void one2manybyOne(){

           //1、获取实体操作对象

           EntityManager manager = JPAUtils.getEntityManager();

          

           //JPQL是通过fetch这个关键词,在查询学生的信息的时候,一起将分数也查询出来

           //最终执行的sql就只有一条

           TypedQuery<Student> query = manager.createQuery("select distinct s from Student s inner join fetch s.scores", Student.class);

          

           List<Student> students = query.getResultList();

           for (Student student : students) {

               System.out.println("学生姓名:"+student.getStuName()+",学生id:"+student.getStuId());

              

               List<Score> scores = student.getScores();

               for (Score score : scores) {

                  System.out.println("科目:"+score.getScoSubject()+",分数:"+score.getScoScore());

               }

               System.out.println("---------------------------------");

           }

        }

    执行结果:

     

    执行过程中,确实一条sql语句!!!

    解决了频繁查询数据库,带来的数据库性能损耗。

  • 相关阅读:
    第十一周课程总结
    第十周学习总结
    第九周课程总结&实验报告(七)
    第八周课程总结&实验报告(六)
    第七周总结&第五次实验报告
    第六周学习总结&第四次实验报告
    课程总结
    第十四周课程总结&实验报告(简单记事本的实现)
    第十三周课程总结
    第十二周
  • 原文地址:https://www.cnblogs.com/meizhoulqp/p/11313430.html
Copyright © 2011-2022 走看看