1、ORM思想
概述:ORM(Object Relational Mapping),对象关系映射。
主要目的:操作实体类对象就等同于操作数据库表。
需要建立两个关系:
- 建立实体类和数据库表之间的映射
- 建立实体类属性和数据库表字段之间的映射
不再关注:
sql语句(自动创建)
实现了ORM思想的框架:mybatis、hibernate
2、JPA规范
概述:Java Persistence API,Java的持久化API。
那么,为什么会有“规范”的出现呢?拿JDBC规范来说,在出现它之前,我们都是跟数据库面对面打交道,但是,市面上有非常多的数据库厂商,每个数据库厂商提供的操作数据库的方式是不同的,对于开发者而言,学习一个新的数据库需要消耗极大的学习成本,那么这个时候,JDBC规范就来了,它要求所有的数据库厂商都去实现这个规范(数据库驱动),而我们开发者只需要知道如何使用这个规范进行编程,并且在项目中导入数据库驱动,就可以达到操作不同数据库的目的。而我们的JPA规范也是如此,起初市面上有很多的ORM框架,但是各个框架的实现方式有很大不同,这给我们学习框架造成了很大的困扰,后来SUN公司就提出了JPA规范,让我们的ORM框架厂商都去实现这套规范,而我们只需要掌握这套规范即可。其实就是面向接口编程,我们不需要知道它的具体实现,只需要调用接口或者抽象类的方法即可。
3、案例说明
创建数据库表
/*创建客户表*/
CREATE TABLE cst_customer (
cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
cust_name varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
cust_source varchar(32) DEFAULT NULL COMMENT '客户信息来源',
cust_industry 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 AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
创建maven工程并导入坐标
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.0.7.Final</project.hibernate.version>
</properties>
<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>
<!-- c3p0 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!-- log日志 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- Mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
编写Jpa的核心配置文件,需要注意两个点:
1、位置:需要放置在类路径下的META-INF文件夹下
2、命名:必须是persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--
持久化单元:
name:持久化单元的名称
transaction-type:事务管理的方式
JTA:分布式事务管理
RESOURCE_LOCAL:本地事务管理
-->
<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.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="123456"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
<!--
可选配置:配置Jpa实现方(hibernate)的配置信息
显示sql:false|true
自动创建数据库表:
create: 程序运行时,自动创建表(如果有表,先删除表再创建表)
update: 程序运行时,自动创建表(如果有表,则不会创建表)
none: 不会创建表
-->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>
创建实体类
package com.hzp.entity;
import javax.persistence.*;
/**
* 客户的实体类
* 配置映射关系:
* 1、配置实体类和数据库表的映射关系
* @Entity:声明实体类
* @Table:配置实体类和表的映射关系
* name:数据库表的名称
* 2、配置实体类属性和数据库表字段的映射关系
* @Id:声明主键
* @GeneratedValue:配置主键的生成策略
* GenerationType.IDENTITY 自增
* @Column:配置属性和字段的映射关系
* name:字段名称
*/
@Entity
@Table(name = "cst_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Integer custId;//客户主键
@Column(name = "cust_name")
private String custName;//客户名称
@Column(name = "cust_source")
private String custSource;//客户来源
@Column(name = "cust_industry")
private String custIndustry;//客户所属行业
@Column(name = "cust_level")
private String custLevel;//客户级别
@Column(name = "cust_address")
private String custAddress;//客户地址
@Column(name = "cust_phone")
private String custPhone;//客户联系方式
public Customer(Integer custId, String custName, String custSource, String custIndustry, String custLevel, String custAddress, String custPhone) {
this.custId = custId;
this.custName = custName;
this.custSource = custSource;
this.custIndustry = custIndustry;
this.custLevel = custLevel;
this.custAddress = custAddress;
this.custPhone = custPhone;
}
public Customer() {
}
public Integer getCustId() {
return custId;
}
public void setCustId(Integer custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custAddress='" + custAddress + '\'' +
", custPhone='" + custPhone + '\'' +
'}';
}
}
测试添加操作
/**
* Jpa的操作步骤:
* 1、加载配置文件,获取实体管理器工厂对象
* 2、通过实体管理器工厂获取实体管理器
* 3、获取事务对象,开启事务
* 4、增删查改
* 5、提交事务(回滚事务)
* 6、释放资源
*/
@Test
public void testSave(){
//1、加载配置文件,获取实体管理器工厂对象(参数是持久化单元的名称)
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2、通过实体管理器工厂获取实体管理器
EntityManager em = factory.createEntityManager();
//3、获取事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//4、增删查改
Customer customer=new Customer();
customer.setCustName("吴航");
customer.setCustAddress("江西");
em.persist(customer);
//5、提交事务(回滚事务)
tx.commit();
//6、释放资源
em.close();
factory.close();
}
控制台输出的sql语句:
Hibernate: drop table if exists cst_customer
Hibernate: create table cst_customer (cust_id integer not null auto_increment, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id))
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)
可以看到,在进行插入之前,会先删除并重新创建表,因为我们前面在配置文件中配置了如下:
<property name="hibernate.hbm2ddl.auto" value="create"/>
4、主键生成策略
我们前面在实体类的custId
属性上配置了一个@GeneratedValue
注解,该注解用于配置主键的生成策略。该注解有一个strategy
属性,这个属性有如下的可选值:
- GenerationType.IDENTITY:自增,要求数据库底层必须支持自增,比如mysql
- GenerationType.SEQUENCE:序列,要求数据库底层必须支持序列,如oracle
- GenerationType.TABLE:jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
- GenerationType.AUTO:由程序自动的帮助我们选择主键生成策略
现在我们将strategy
修改为GenerationType.TABLE,来测试一下:
控制台输出:
Hibernate: drop table if exists cst_customer
Hibernate: drop table if exists hibernate_sequences
Hibernate: create table cst_customer (cust_id integer not null, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id))
Hibernate: create table hibernate_sequences (sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name))Hibernate: select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update
Hibernate: insert into hibernate_sequences (sequence_name, next_val) values (?,?)
Hibernate: update hibernate_sequences set next_val=? where next_val=? and sequence_name=?
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, cust_id) values (?, ?, ?, ?, ?, ?, ?)
可以看到,会创建两张表cst_customer
和hibernate_sequences
,可以看到,我们cst_customer
的主键不再是自增的了,它的主键id值来源于我们这里的hibernate_sequences
表,使用这个表为我们进行主键自增。
然后我们再将strategy
修改为GenerationType.AUTO来测试:
控制台输出:
Hibernate: drop table if exists cst_customer
Hibernate: drop table if exists hibernate_sequence
Hibernate: create table cst_customer (cust_id integer not null, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id))
Hibernate: create table hibernate_sequence (next_val bigint)
Hibernate: insert into hibernate_sequence values ( 1 )Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, cust_id) values (?, ?, ?, ?, ?, ?, ?)
可以看到,它也会为我们生成两张表。
5、抽取工具类
/**
* Jpa工具类
* 用于解决创建实体管理器工厂对象浪费资源的问题
*/
public class JpaUtil {
private static EntityManagerFactory factory;
static {
factory= Persistence.createEntityManagerFactory("myJpa");
}
/**
* 获取实体管理器
*/
public EntityManager getEntityManager(){
return factory.createEntityManager();
}
}
测试工具类
@Test
public void testSave(){
//1、加载配置文件,获取实体管理器工厂对象(参数是持久化单元的名称)
// EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2、通过实体管理器工厂获取实体管理器
// EntityManager em = factory.createEntityManager();
EntityManager em = JpaUtil.getEntityManager();
//3、获取事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//4、增删查改
Customer customer=new Customer();
customer.setCustName("吴航");
customer.setCustAddress("江西");
em.persist(customer);
//5、提交事务(回滚事务)
tx.commit();
//6、释放资源
em.close();
// factory.close();
}
6、CRUD
根据id查询客户,查询前先将
hibernate.hbm2ddl.auto
的值修改为update
@Test
public void testFind(){
//1.通过工具类获取实体管理器
EntityManager em = JpaUtil.getEntityManager();
//2.创建事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.根据id查询客户
Customer customer = em.find(Customer.class, 1);
System.out.println(customer);
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
控制台输出:
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_ from cst_customer customer0_ where customer0_.cust_id=?
Customer{custId=1, custName=‘吴航’, custSource=‘null’, custIndustry=‘null’, custLevel=‘null’, custAddress=‘江西’, custPhone=‘null’}
sql语句看起来好像很复杂,其实就是起了俩别名,去掉等价于:select * from cst_customer where cust_id=1
除了通过find查询外,也可以通过getReference方法进行查询
@Test
public void testReference(){
//1.通过工具类获取实体管理器
EntityManager em = JpaUtil.getEntityManager();
//2.创建事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.根据id查询客户
Customer customer = em.getReference(Customer.class, 1);
// System.out.println(customer);
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
find()
和getReference()
在使用和功能上是一样的,区别在于:
find()
获取的是实体类对象,而getReference()
获得的是代理对象。我们可以打断点进行调试。
find()
是立即加载,而getReference()
是延迟加载(懒加载),所谓的立即加载是在调用find
的时候就会发出sql语句,而懒加载则是在使用结果对象的时候才会发出sql语句,我们可以将输出结果对象的语句注释掉,看看是否会打印sql。
删除客户
/**
* 删除客户
*/
@Test
public void testDelete(){
//1.获取实体管理器
EntityManager em = JpaUtil.getEntityManager();
//2.创建事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.删除操作
//需要先查询是否存在,然后才删除
Customer customer = em.find(Customer.class, 1);
em.remove(customer);
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
更新客户
/**
* 更新客户
*/
@Test
public void testUpdate(){
//1.获取实体管理器
EntityManager em = JpaUtil.getEntityManager();
//2.创建事务对象,开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
//3.删除操作
//需要先查询是否存在,然后才删除
Customer customer = em.find(Customer.class, 1);
em.merge(customer).setCustName("天行者");//修改客户姓名
//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
7、JPQL
查询全部
/**
* 查询全部
* sql:select * from cst_customer
* jpql:from com.hzp.entity.Customer
*/
@Test
public void testQuery(){
EntityManager em = JpaUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String jpql="from com.hzp.entity.Customer";//构建jpql语句
Query query = em.createQuery(jpql);//创建Query对象
List list = query.getResultList();
for (Object obj : list) {
System.out.println(obj);
}
tx.commit();
em.close();
}
倒序查询
/**
* 倒序查询:
* sql:select * from cst_customer order by cust_id desc
* jpql: from Customer order by custId desc 包名可以省略
*/
@Test
public void testOrder(){
EntityManager em = JpaUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String jpql="from Customer order by custId desc";//构建jpql语句
Query query = em.createQuery(jpql);//创建Query对象
List list = query.getResultList();
for (Object obj : list) {
System.out.println(obj);
}
tx.commit();
em.close();
}
统计查询
/**
* 统计查询
* sql:select count(cust_id) from cst_customer
* jpql:select count(custId) from Customer
*/
@Test
public void testCount(){
EntityManager em = JpaUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String jpql="select count(custId) from Customer";//构建jpql语句
Query query = em.createQuery(jpql);//创建Query对象
Object result = query.getSingleResult();
System.out.println(result);
tx.commit();
em.close();
}
分页查询
/**
* 分页查询:
* sql:select * from cst_customer limit ?,?
* jpql:from Customer
*/
@Test
public void testPage(){
EntityManager em = JpaUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String jpql="from Customer";//构建jpql语句
Query query = em.createQuery(jpql);//创建Query对象
query.setFirstResult(0);//设置起始索引
query.setMaxResults(2);//设置最大结果条数
List list = query.getResultList();
for (Object obj : list) {
System.out.println(obj);
}
tx.commit();
em.close();
}
条件查询
/**
* 条件查询
* sql:select * from cst_customer where cust_name=?
* spql:from Customer where custName=?
*/
@Test
public void testCondition(){
EntityManager em = JpaUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String jpql="from Customer where custName like ?";//构建jpql语句
Query query = em.createQuery(jpql);//创建Query对象
query.setParameter(1, "天行者%");
List list = query.getResultList();
for (Object obj : list) {
System.out.println(obj);
}
tx.commit();
em.close();
}