java 实体类 临时注解_JPA:Java持久层API--配置流程

一、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实现框架 

二、 入门示例

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

2.1 配置流程图

2f0913d5cd38cb513780228e33d3efc8.png
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文件,直接使用

9e6cc217fa4a707718986314053b1766.png

配置信息如下:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="Java Persistence API: XML Schemas" 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="Java Persistence API: XML Schemas" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="Java Persistence API: XML Schemas 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 {@Testpublic 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(); } 
}

测试结果

a92fe813cfdbdbdfc6007850734b59c5.png

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

2.4 使用JPA的好处

使用JPA,可以直接使用对象操作数据库,由框架根据映射的关系生成SQL。不用开发人员编写。这样做,开发人员就不用编写SQL语句了。 
问题:这样有什么好处呢? 
答:不同的数据库的SQL语法是有差异,如果不需要编写SQL语句。就屏蔽各种数据库SQL的差异。那么,编写的代码就可以一套代码兼容多种数据库!!!! 

三、JPA实现CRUD

修改StudentDAOTest类,测试crud操作

//通过OID删除@Testpublic 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();}//更新@Testpublic void merge(){//1.获得实体管理类对象EntityManager entityManager = JPAUtils.getEntityManger();//2.打开事务EntityTransaction transaction = entityManager.getTransaction();//3.启动事务transaction.begin();//4.创建数据Student s=new Student();s.setStuName("李四");//更新必须要有一个OIDs.setStuId(3L);//5.更新entityManager.merge(s);//6。提交transaction.commit();//7.关闭entityManager.close();}//通过OID获得数据@Testpublic void find(){//1.获得实体管理类对象EntityManager entityManager = JPAUtils.getEntityManger();//通过OID查询数据Student student = entityManager.find(Student.class, 1L);System.out.println(student.getStuName());entityManager.close();}//通过OID获得数据@Testpublic void getReference(){//1.获得实体管理类对象EntityManager entityManager = JPAUtils.getEntityManger();/*** getReference()和find()方法的区别:* getReference基于懒加载机制,即需要使用对象的时候,才执行查询。*/Student student = entityManager.getReference(Student.class, 1L);System.out.println(student.getStuName());entityManager.close();}

四、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查询接口

五、JPA多表关联查询

多个关联查询作用(导航查询):就是实现使用一个实体类对象查询多个表的数据。 配置多表联系查询必须有两个步骤; 
(1)、在实体类里面建立表与表之间的关系。 
(2)、在实体类配置关联关系,JPA使用注解配置 

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

6d2aaec53ea3f976aa312f71e26a0bf3.png

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

5.1 一对多实现 (单向)

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

5.1.1 说明

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

8e117813a44c360036d2392d8548c9fe.png

所以,通过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();}
// 补全get、set方法
}

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(); 
}

查询结果:

0daa4208d9dd2249b95dca1bebb9c688.png

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

5.2 多对一实现 (单向)

5.2.1 说明

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

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

9c2de6273d0c1ec41fd40baee3f20009.png

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 第四步:测试

@Testpublic 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(); }

查询结果:

9148e441736c4633dbe39f32b1102f68.png

多对一配置成功!!!

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查询学生的信息,通过也获得学生对应的学生身份信息。

7044e010ec540b0105628aa88e8a81be.png

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 第四步:测试

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

@Testpublic 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(); }@Testpublic 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)测试学生关联身份信息

测试结果:

c7ffe9b82306d5678c1848575825a564.png

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

c0225682bc799e85ee63d70d398ac8cc.png

一对一配置成功!!!

5.5 配置多对多

5.5.1 说明

需求:

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

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

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

1341fc502b7abf4fd7d7703aa9d8b718.png

如上图所示:

如果要从学生表的信息获得教师表的信息。必须需要三个条件 
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:测试学生关联教师

测试代码

@Testpublic 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(); }

测试结果:

9d3ccb48b9d1e7deb78eda71fb82eaed.png

5.5.2.4.2 Step2:测试教师关联学生

测试代码

@Testpublic 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(); }

测试结果:

723004a5ad04e28fe373d2b9db10a9cc.png

多对多配置成功!!!

六、JPA逆向工程

6.1 说明

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

目的:提高开发的效率

d0a71988fdb3f639361ae6558f0b2a77.png

6.2 步骤

6.2.1 第一步:创建JPA项目

(1)创建项目

52ccc404b2cb77543f417708011bdd37.png

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

4fe2ba1c490fe7227674bc4d444ac3ef.png

(3)完成创建

dbfc20165fe3e1c90eeb32f81c9d7991.png

6.2.2 第二步:生成JPA代码

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

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

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

4d299b32d711f5c7e34a686cc1c8d2c8.png

(2)指定数据库类型

b188e6a87cb2d8aa2e07ad3160a5d100.png

(3)新建数据库驱动

bd661a50bc1ca95f931e73661f53dd12.png

(4)配置驱动信息

驱动版本号

3af94f7c1ccfbab318ef458c0d476f9a.png

加载驱动jar包

cae3d30527aea08fd760885ff499e091.png

配置jdbc四要素

452fccfc1cba5d5ba64b588c661860c6.png

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

(1)配置表关联关系

选择表,全选即可。

bb3d83f161b62e6c5f93955fbfd2fdf4.png

配置Student、Score一对多

04d131670a98f323f6758b2112c32655.png

776322f0033e4768ebd8a0a38303aae4.png

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

fa4996be9c1f79875a3d8171784cbfb4.png

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

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

d97784bad42693783671550e2a23529b.png

指定实体类的生成属性

4653d9ff341c564a51ba861b8393d23b.png

指定实体类的类名

4f765b9527a830034fe710513ec5a126.png

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

6.2.2.4 Step4:生成代码

197a9931ab9f9276285d1fd7de162222.png

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

说明:Maven项目是可以导入jar包到本地的。
方法:打开DOS窗口,进入项目的pom文件所在目录,执行命令:
mvn dependency:copy-dependencies
前提:已经配置了Maven环境变量。

fe0dfdda2d1ecb04aca05223b7df208e.png

6.2.4 第四步:更新项目

cf2926ab2df36bfcf126e14aaec83cbb.png

七、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来解决。

@Testpublic 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("---------------------------------");}}

执行结果:

459ad7d83a2a76b4f8fa2ed6bc4c0508.png

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/245547.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android新架构,Android新架构组件 LifeCycles 简介

一、前言为了使开发者能尽快在 Android 平台上开发出高质量的项目&#xff0c;Android 官方推出了 Android Jetpack 项目&#xff0c;旨在从基础&#xff0c;架构&#xff0c;行为以及界面 4 大方面体系化地为我们提供组件级别的支持。当然&#xff0c;在实际开发过程中&#x…

领域驱动设计 pdf_什么是领域驱动设计?

什么是领域驱动设计&#xff1f;你可能使用领域驱动设计(DDD)开发了一些项目。你可能很满意&#xff0c; 使用领域模型来开发领域业务。并且得意地展示给你的同事看&#xff0c;他们会说“666”。但有的时候你使用领域模型你总觉得哪儿有点不对劲。你会嘀咕你可能遗漏了什么。 …

Android四级缓存,RecyclerView 源码四级缓存原理

入口我们从使用功能上去读取源码&#xff0c;通常的用法是这个样子-> 我们设置layoutmanager&#xff0c;GridLayouManager 继承LinearLayoutManager&#xff0c;所以我们就LinearLayoutManager 为基准查看rv.layoutManager GridLayoutManager(this,5)rv.addItemDecoration…

织梦自定义html文本,织梦自定义标签dede:sql根据自定义字段填的文章id获取相关文章...

这篇文章主要为大家详细介绍了织梦自定义标签dede:sql根据自定义字段填的文章id获取相关文章&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借鉴。有的时候我们需要通过织梦的dede:sql据自定义字段填的文章id获取相关文…

python 杀死子进程_Python:当父异常终止时,如何杀死子进程?

小编典典 呵呵&#xff0c;我昨天自己在研究这个&#xff01;假设您无法更改子程序&#xff1a; 在Linux上&#xff0c;prctl(PR_SET_PDEATHSIG,...)可能是唯一可靠的选择。&#xff08;如果绝对有必要终止子进程&#xff0c;那么您可能希望将终止信号设置为SIGKILL而不是SIGTE…

html评论置顶功能,微信公众号精选留言评论怎么置顶显示?功能在哪里设置?...

微信公众号精选留言怎么置顶&#xff1f;微信公众号留言功能新增了置顶精选留言的设置&#xff0c;那么微信公众号留言功能在哪里设置呢&#xff1f;下文小乐哥给大家介绍一下&#xff01;微信公众号精选留言怎么置顶&#xff1f;微信公众平台悄然上线了一个新功能&#xff0c;…

python函数增强代码可读性_写Python必须知道的这几个代码技巧!你会吗?

Day09 函数的初始 函数&#xff1a;函数是以功能为导向&#xff0c;一个函数封装一个功能。登录&#xff0c;注册&#xff0c;文件的改的操作。。。 函数减少代码的重复性&#xff0c;增强了代码的可读性&#xff1b; 获取任意一个字符串的元素的个数 s1 "xiaomingxiaoho…

shell脚本发邮件内容html,[转]Shell脚本中发送html邮件的方法

作为运维人员&#xff0c;免不了要编写一些监控脚本&#xff0c;并将监控结果及时的发送出来。那么通过邮件发送是比较常用的一种通知方式了。通常的&#xff0c;如果需要发送的内容是简单的文本文件&#xff0c;那么使用/bin/mailx就可以了&#xff0c;但是如果想要发送更复杂…

learn python app v3_‎App Store 上的“Learn Python and Scratch”

Learn “Python and Scratch Programming” from AI driven coach and satisfy your thirst for knowledge. App offers bite sized videos, quizzes and AI driven coach to help you become smarter and become great. Just 60 minutes a week can help you become great in …

计算机应用基础知识竞赛题,计算机基础知识题库

随着科学技术的进步&#xff0c;计算机已逐渐渗入人们的生活中&#xff0c;相应的计算机知识是需要具备的&#xff0c;那么你对计算机基础知识了解多少呢?以下是由学习啦小编整理关于计算机基础知识题库的内容&#xff0c;希望大家喜欢!计算机基础知识题库一、单选题1、 第一台…

inspect python_python之inspect模块

inspect模块主要提供了四种用处&#xff1a; 1.对是否是模块、框架、函数进行类型检查 2.获取源码 3.获取类或者函数的参数信息 4.解析堆栈 一、type and members 1. inspect.getmembers(object[, predicate]) 第二个参数通常可以根据需要调用如下16个方法&#xff1b; 返回值为…

HTML打开网页拒绝访问,192.168.1.1拒绝访问怎么办?

问&#xff1a;为什么设置路由器时&#xff0c;在浏览器中输入192.168.1.1&#xff0c;结果显示拒绝访问&#xff0c;这个问题怎么解决&#xff1f;答&#xff1a;如果是在设置路由器的时候&#xff0c;登录192.168.1.1被拒绝访问&#xff0c;多半是你自己操作有问题导致的&…

python中goto的用法_python3里用goto

python里用goto也是小Pa最近做的项目里的一个需求。python不像C有自带的goto, 需要用额外的包&#xff0c;目前为止&#xff0c;小pa只看到2个goto的包: 这2个小Pa都下载试用过&#xff0c;goto因为开发的时候比较早&#xff0c;对于python3的支持不太好&#xff0c;不推荐使用…

delphi打印html文件路径,Delphi获取文件名、不带扩展名文件名、文件所在路径、上级文件夹路径的方法...

1.获取不带扩展名的文件名方法&#xff0c;利用ChangeFileExt函数修改传入参数的扩展为空&#xff0c;并不会对文件本身产生变更。ChangeFileExt(ExtractFileName(‘D:\KK\Test\123.txt‘),‘‘); //返回 1232.获取上级文件夹路径的方法。ExtractFileDir(‘D:\KK\Test\‘)‘..‘…

gitlab git clone 输入密码_gitlab1:部署gitlab

1、配置yum源vim /etc/yum.repos.d/gitlab-ce.repo复制以下内容&#xff1a;[gitlab-ce]nameGitlab CE Repositorybaseurlhttps://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el$releasever/gpgcheck0enabled12、更新本地yum缓存sudo yum makecache3、安装GitLab社区版sudo y…

计算机专业英语第五章ppt,计算机专业英语第五章.ppt

计算机专业英语第五章Background The Internet protocols are the worlds most popular open-system (nonproprietary) protocol suite because they can be used to communicate across any set of interconnected networks and are equally well suited for LAN and WAN comm…

python播放在线音乐_Python实现在线音乐播放器

最近这几天&#xff0c;学习了一下python&#xff0c;对于爬虫比较感兴趣&#xff0c;就做了一个简单的爬虫项目&#xff0c;使用Python的库Tkinsert做了一个界面&#xff0c;感觉这个库使用起来还是挺方便的&#xff0c;音乐的数据来自网易云音乐的一个接口&#xff0c;通过re…

golang如何打印float64的整数部分_2020-08-10:如何不用加减乘除求浮点数的2倍值?...

福哥答案2020-08-10&#xff1a;浮点数符号位阶码尾数&#xff0c;阶码加1就是浮点数的2倍值。代码用golang编写&#xff0c;如下&#xff1a;package test33_addimport ( "fmt" "math" "testing")/*//https://www.bbsmax.com/A/6pdDX7…

五年级数学上册用计算机探索规律,人教版小学五年级数学上册《用计算器探索规律》课后反思...

当前,新课程改革强调学生学习方式的转变.高效课堂是课程改革过程中有效学习方式之一.在高效课堂中,孩子们能发挥自己潜能、展示自己的才能,提高了孩子们的学习兴趣.如何让高效课堂焕发光彩能&#xff1f;一、合理分组,恰当分工合理分组是高效课堂顺利进行的前提.在以前的学习过…

mysql varchar 非空判断_工资从1万到3万,你还差mysql数据库优化之系列三

查询性能的优化优化查询分析的步骤:1.应用查询是否检索超过需要的数据2.mysql服务器是否在分析超过需要的数据正确使用索引:1.like语句操作一般不使用%或_开头例如: select * from tableName where name like %cn;只能使用like aaa%;2.组合索引例如索引index index_name (a, b,…