一:orm思想,hibernate,jpa概述,jpa基本操作
1.orm思想
- 回顾JDBC如何完成基本的保存操作
-
操作繁琐
-
需要挨个对占位符进行赋值,麻烦
public class User{ private String userId; private String userName; private String address; //省略setter,getter方法 }
create table t_user( id int auto_increament, username varchar, address varchar )
//SQL语句 String sql="insert into t_user(username,address) values(?,?)"; //1.过去连接 Connection conn=DriverManager.getConnection(username,password,url) //2.创建statement对象 PrepareStatement pst=conn.prepareStatement(sql); //3.对占位符进行赋值 pst.setString(1,user.getUserName()) pst.setString(2,user.getAdress()) //4.发送查询 pst.executeUpdate();
-
如何解决传统JDBC中遇到的问题
-
如何建立实体与表和实体属性与表中字段的关系 (QRM思想:即操作实体类相当于操作数据库表)
- 实体类与表的映射关系
- 实体类属性与数据库表字段的映射关系
实现了orm思想的框架:mybatis,hibernate
2.实现啦orm思想的框架:hibernate
hibernate是一个开源的对象映射框架,它对JDBC进行了非常轻量级的封装,它将POJO与数据表建立映射厝没完成一个全自动的orm框架
3.JPA规范
jpa规范,实现jpa规范,内部是由接口和抽象类组成的(并不干活,干活的是每个狂阶的生产厂商,例如hibernate)
4.JPA基本操作
案例: 对客户的基本操作
- 客户表:
DROP TABLE IF EXISTS `cst_customer`;
CREATE TABLE `cat_customer` (
`cust_id` bigint(32) NOT NULL COMMENT '客戶编码',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industy` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '客户联系电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1. 搭建JPA环境的过程
1.1 创建Maven工程导入坐标
<dependencies>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--hibernate对jpa的支持包-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
1.2 配置JPA的核心配置文件
JPA 规范要求在类路径的 META-INF 目录下放置persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--需要配置:persistence-unit
持久化单元:
name:持久化单元名称指定 JPA 的事务处理策略
JTA:分布式事务管理
RESOURCE_LOCAL:本地事务管理
RESOURCE_LOCAL:默认值,数据库级别的事务,只能针对一种数据库,不支持分布式事务。
如果需要支持分布式事务,使用JTA:transaction-type="JTA"
-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<!--数据库信息:
用户名:javax.persistence.jdbc.user
密码:javax.persistence.jdbc.password
驱动:javax.persistence.jdbc.driver
数据库地址:javax.persistence.jdbc.url
-->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3305/test"/>
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="mmwan980815" />
<!--orm框架的基本信息:
可选配置:jpa实现方(hibernate)的配置信息
hibernate.show_sql,:显示SQL,true|false
hibernate.hbm2ddl.auto:自动创建创建数据库表,
create:程序运行时创建数据库表(如果有表,先删除再创建)
update:程序运行时创建数据库表(如果有表,不会创建表)
none:不会创建表
-->
<property name="hibernate.hbm2ddl.auto" value="create"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
1.3编写客户实体类
public class Customer {
private Long custId;//客户主键
private String custName;//客户名称
private String custSource;//客户来源
private String custLevel;//客户级别
private String custIndustry;//客户所属行业
private String custPhone;//客户电话
private String custAddress;//客户地址
//此处省略setter,getter,toString方法
}
1.4配置实体类和表,勒种属性和表中字段的映射关系
/**
* 客户的实体类:
* 配置映射关系
* 1.实体类和表的映射关系
* @Entity:声明实体类
* @Table:配置实体类和表的映射关系
* 2.实体类中属性和表中字段的映射关系
*/
@Entity
@Table(name ="cst_customer")
public class Customer {
@Id//声明该属性对应数据库表中的主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键生成策略
@Column(name ="cust_id")//配置属性和字段的映射关系
private Long custId;//客户主键
@Column(name = "cust_name")
private String custName;//客户名称
@Column(name = "cust_source")
private String custSource;//客户来源
@Column(name ="cust_industy" )
private String custIndustry;//客户所属行业
@Column(name = "cust_level")
private String custLevel;//客户级别
@Column(name = "cust_phone")
private String custPhone;//客户电话
@Column(name = "cust_address")
private String custAddress;//客户地址
//此处省略setter,getter,toString方法
}
2. 完成基本的CRUD
2.1案例+CRUD操作步骤
保存一个客户到数据库中
利用JPA进行CRUD的操作步骤:
1.加载配置文件创建工厂(实体管理器工厂)对象
2.通过实体管理类工厂获取实体管理器
3.获取事务对象,开启事务
4.完成增删改查操作
5.提交事务(回滚事务)
6.释放资源
public class JpaTest {
@Test
public void testSave(){
//1.加载配置文件创建工厂(实体管理器工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2.通过实体管理类工厂获取实体管理器
EntityManager manager = factory.createEntityManager();
//3.获取事务对象,开启事务
EntityTransaction tx = manager.getTransaction();//获取事务对象
tx.begin();//开启事务
Customer customer=new Customer();
customer.setCustName("name");
customer.setCustIndustry("industry");
//4.完成增删改查操作
manager.persist(customer);//保存操作
// 5.提交事务(回滚事务)
tx.commit();
//6.释放资源(反向关闭)
manager.close();
factory.close();
}
}
运行结果:
5.主键生成策略
strategy
1. GenerationType.IDENTITY:自增,数据库必须支持自增长得方式,对ID自增(mysql)
2. GenerationType.SEQUENCE:序列,数据库必须支持序列(Oracle)
3. GenerationType.TABLE:jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自
4. GenerationType.AUTO:有程序自助帮我们选择主键生成策略
@Id//声明该属性对应数据库表中的主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//主键生成策略
@Column(name ="cust_id")//配置属性和字段的映射关系
二:Jpa基本操作
1. jpa的操作步骤:
1.1加载配置文件创建工厂(实体管理器工厂)对象
Persistence的静态方法:
createEntityManagerFactory(持久化单元名称)
作用:创建实体管理器工厂
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
- 内部维护了数据信息,维护了缓存信息,维护了所有的实体管理器对象
- 在创建EntityManagerFactory 的过程中会根据配置创建数据库表
- EntityManagerFactory 的创建过程比较浪费资源
- 特点:线程安全,多个线程同时访问同一个EntityManagerFactory 时不会出现线程安全的问题
- 如何解决EntityManagerFactory 创建过程耗时的问题?
思路:创建一个公共的EntityManagerFactory 对象
1.2.通过实体管理类工厂获取实体管理器
EntityManagerFactory :实体管理器工厂,用来获取/创建EntityManager (实体管理器)
createEntityManager();
EntityManager manager = factory.createEntityManager();
1.3获取事务对象,开启事务
EntityManager 对象:实体类管理器
persist():保存
merge():更新
remove():删除
find()/getRefrence():根据id查询
Transaction 对象:事务
begin():开启事务
commit():提交事务
rollback():回滚
1.4完成增删改查操作
1.5提交事务(回滚事务)
1.6释放资源
2.如何解决EntityManagerFactory 创建过程耗时的问题?
“1.1有提到EntityManagerFactory 的创建过程比较浪费资源,多个线程同时访问同一个EntityManagerFactory 时不会出现线程安全问题”,因此我们可以试着创建一个公共的EntityManagerFactory 对象
2.1 jpa工具类
/**
*解决实体类管理器工厂的浪费资源和耗时问题
* 通过静态代码块的执行,当程序第一次访问次工具类的时候,创建一个公共的实体管理器工厂
* 第一次访问getEntityManager方法,经过静态代码块创建一个factory对象,在调用方法创建一个EntityManager对象
* 第一次访问getEntityManager方法,直接通过已经创建好的factory对象,创建一个EntityManager对象
*/
//JPA工具类
public class JpaUtils {
private static EntityManagerFactory factory;
static {
//1.加载配置文件,创建EntityManagerFactory工厂
factory= Persistence.createEntityManagerFactory("myJpa");
}
/**
* 获取EntityManager对象
*/
public static EntityManager getEntityManager(){
return factory.createEntityManager();
}
}
3.基本操作
3.1 增加客户(persist)
@Test
public void testSave2(){
// //1.加载配置文件创建工厂(实体管理器工厂)对象
// EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2.通过写好的工具类获取实体管理器
EntityManager manager = JpaUtils.getEntityManager();
//3.获取事务对象,开启事务
EntityTransaction tx = manager.getTransaction();//获取事务对象
tx.begin();//开启事务
Customer customer=new Customer();
customer.setCustName("name");
customer.setCustIndustry("industry");
//4.完成增删改查操作
manager.persist(customer);//保存操作
// 5.提交事务(回滚事务)
tx.commit();
//6.释放资源(反向关闭)
manager.close();
// factory.close();此处我们不需要再关闭工厂,关闭后其他方法将无法使用
}
3.2 根据ID查询客户(find / getReference)
find(Customer.class, 1)
getReference(Customer.class, 1)得
这两个方法得到的结果是一样的,均可查询到我们要的数据
@Test
public void testFind(){
//1.通过工具类获取EntityManager实体类管理器对象
EntityManager manager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = manager.getTransaction();
tx.begin();
//3.增删查改
/**
* find():查询数据的结果需要包装的实体类类型的字节码
* class属性:查询数据的结果需要包装的实体类类型的字节码
* id属性:查询主键的取值
*/
Customer customer = manager.find(Customer.class, 1);//
// Customer customer = manager.getReference(Customer.class, 1);
System.out.println(customer);
//4.提交事务
tx.commit();
//5.释放资源
manager.close();
}
3.3 根据ID删除客户(remove)
- 先根据ID进行查询,得到要删除的客户对象
- 调用remove方法,传入第一步查询得到的客户对象,从而根据该对象的ID进行删除操作
/**
* 根据ID删除客户
* remove:
* o:要删除的对象
*/
@Test
public void tesRemove() {
//1.通过工具类获取EntityManager实体类管理器对象
EntityManager manager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = manager.getTransaction();
tx.begin();
//3.增删查改
/**
* 1.根据ID查询客户
* 2.调用remove传入查询的客户对象从而进行删除
*/
Customer customer = manager.getReference(Customer.class, 1);
manager.remove(customer);
//4.提交事务
tx.commit();
//5.释放资源
manager.close();
}
3.4 客户更新操作(merge)
- 根据ID查询客户
- 更新客户
a. 更新客户属性
b. 调用merge方法,传入更新了属性的客户对象
/**
* 客户更新操作
*/
@Test
public void tesUpdate() {
//1.通过工具类获取EntityManager实体类管理器对象
EntityManager manager = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = manager.getTransaction();
tx.begin();
//3.增删查改
/**
* 1.根据ID查询客户
* 2.更新客户
* 更新客户属性
* 调用merge方法,传入更新了属性的客户对象
*/
Customer customer = manager.getReference(Customer.class, 2);
customer.setCustAddress("西安工程大学");
manager.merge(customer);
//4.提交事务
tx.commit();
//5.释放资源
manager.close();
}
4. 延迟加载与立即加载
3.2中根据Id查询客户信息时,我们可以发现find / getReference均可得到我们想要的结果,那它们区别是什么呢?
4.1 使用find方法查询(立即加载)
- 查询的对象就是当前对象本身
- 在调用find方法时,就会发送SQL语句查询数据库
4.2使用getReference方法查询(延迟加载)
- 查询的对象就是当前对象的一个代理对象
- 在调用getReference方法时,不会立即发送SQL语句查询数据库,当我们调用查询结果对象的视乎,才会发送SQL语句,什么时候用,什么时候发送SQL语句
推荐使用getReference方法进行查询,这样不会产生不必要的查询
三:JPQL中的复杂查询
1.简介
jpql其特征与原生的SQL语句类似,并且完全面向对象,通过类名和属性进行访问,而不是表名和属性
- SQL:查询的是表和表中的字段
- JPQL:查询的是实体类和类中的属性
- JPQL和SQL语句的语法相似
2.基于JPQL的查询
实现步骤:
-
编写jpql语句:from com.yolo.domain.Customer(全限定类名)
String jpql="from com.yolo.domain.Customer";//jpql:from com.yolo.domain.Customer(全限定类名)
-
创建Query查询对象query,query才是执行jpql的对象
Query query = em.createQuery(jpql);
-
对参数赋值
-
发送查询,并封装结果集
List rs = query.getResultList();//list结果集 Object rs = query.getSingleResultList();//单一结果,得到的是一个Object 对象
2.1 查询全部
查询全部
List rs = query.getResultList();
/**
* 查询全部
* jpql:from com.yolo.domain.Customer(全限定类名)
* sql:SELECT * FROM cst_customer;
*/
@Test
public void testFindAll(){
//1.获取EntityManager对象
EntityManager em = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查询全部
String jpql="from com.yolo.domain.Customer";//jpql:from com.yolo.domain.Customer(全限定类名)
//创建Query查询对象query,query才是执行jpql的对象
Query query = em.createQuery(jpql);
//发送查询,并封装结果集
List rs = query.getResultList();
for (Object obj : rs) {
System.out.println(obj);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
根据ID倒序查询全部客户
- SQL:SELECT * FROM cst_customer ORDER BY cust_id DESC;
- JPQL:from Customer order by custId desc
只需修改上述查询全部代码中的jpql语句即可完成倒序查询
2.2 分页查询
- 对参数进行赋值,分页参数
query.setFirstResult(1);//每次从1开始查,不包含1
query.setMaxResults(2);//每次查询两条
/**
* 分页查询
* sql:SELECT * FROM cst_customer LIMIT ?,?;
* jpql:from com.yolo.domain.Customer
*/
@Test
public void testPage(){
//1.获取EntityManager对象
EntityManager em = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查询全部
//a.jpql语句
String jpql="from com.yolo.domain.Customer";
//b.创建Query查询对象query,query才是执行jpql的对象
Query query = em.createQuery(jpql);
//c.对参数进行赋值,分页参数
query.setFirstResult(1);//每次从1开始查,不包含1
query.setMaxResults(2);//每次查询两条
//发送查询,并封装结果集
List rs = query.getResultList();
for (Object r : rs) {
System.out.println(r);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
2.3 统计查询
- sql:SELECT COUNT(cust_id) FROM cst_customer;
- jpql:select count(custId) from Customer
Object rs = query.getSingleResultList();//单一结果,得到的是一个Object 对象
/**
* 统计查询
* sql:SELECT COUNT(cust_id) FROM cst_customer;
* jpql:select count(custId) from Customer
*/
@Test
public void testCount(){
//1.获取EntityManager对象
EntityManager em = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查询全部
String jpql="select count(custId) from Customer";
//创建Query查询对象query,query才是执行jpql的对象
Query query = em.createQuery(jpql);
//发送查询,并封装结果集
Object rs = query.getSingleResult();
System.out.println(rs);
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
2.4 条件查询
案例:查询客户名称以“郭”开头的
- SQL:SELECT * FROM cst_customer WHERE cust_name LIKE “郭%”
- JPQL: from com.yolo.domain.Customer where custName like ?1
注意:like后的占位符要写为 ?---->?1,这是新的JPA样式,否则会出现下方的异常
Caused by:
org.hibernate.QueryException: Legacy-style query parameters (`?`) are no longer supported;
use JPA-style ordinal parameters (e.g., `?1`) instead :
/**
* 条件查询
* 案例:查询客户名称以“郭”开头的
* SQL:SELECT * FROM cst_customer WHERE cust_name LIKE "郭%"
* jpql:from com.yolo.domain.Customer where custName like ?1
*/
@Test
public void testCondition(){
//1.获取EntityManager对象
EntityManager em = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//a.jpql语句
String jpql="from Customer where custName like ?1";
//b.创建Query查询对象query,query才是执行jpql的对象
Query query = em.createQuery(jpql);
/**c.对参数进行赋值,占位符参数.
第一个参数:占位符的索引位置(1开始)
第二个参数:
*/
query.setParameter(1,"郭%");
//发送查询,并封装结果集
List rs = query.getResultList();
for (Object r : rs) {
System.out.println(r);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
2.5 排序
- SQL:SELECT * FROM cst_customer ORDER BY cust_id DESC;
- JPQL:from Customer order by custId desc
List rs = query.getResultList();
/**
* 根据ID倒序查询所有客户
* SQL:SELECT * FROM cst_customer ORDER BY cust_id DESC;
* JPQL:from Customer order by custId desc
*/
@Test
public void testOrders(){
//1.获取EntityManager对象
EntityManager em = JpaUtils.getEntityManager();
//2.开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.查询全部
String jpql="from Customer order by custId desc";
//创建Query查询对象query,query才是执行jpql的对象
Query query = em.createQuery(jpql);
//发送查询,并封装结果集
List rs = query.getResultList();
for (Object obj : rs) {
System.out.println(obj);
}
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}