一、为什么要用Spring Data
将不同的数据存储进行了统一,提升了开发效率,降低了学习成本;针对不同的数据库提供不同的模板对象,通过模板对象来操作对应的数据库。
Spring Data致力为数据访问(DAO)提供熟悉且一致的基于Spring的编程模板:
对于每种持久性存储,通常需要为不同存储库提供对不同CRUD持久化操作。Spring Data为这些持久性存储以及特定实现提供了通用接口(CrudRepository、Paging(分页)AndSorting(排序)Repository)和模板(jdbcTemplate、redisTemplate、RestTemplate、MongoTemplate)。
目的是统一和简化对不同类型持久性存储的访问。
二、JPA
什么是JPA?
相同处:
- JPA是JDBC的升级版
- JDBC和JPA都是一组规范接口
不同处:
-
JDBC是由各个关系型数据库实现的,JPA是由ORM框架实现
ORM:表与java类的映射关系
-
JDBC使用SQL语句和数据库通信,JPA用面向对象方式,通过ORM框架来生成SQL,进行操作
-
JPA在JDBC之上的,JPA要依赖JDBC才能操作数据库
JPA是Sun官方提出的一种ORM规范。
Hibernate与JPA:
JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口是需要实现才能工作的。
Hibernate就是实现了JPA接口的ORM框架。
总结:JPA是一套ORM规范,HIbernate实现了JPA规范!
Hibernate与Mybatis:
Mybaits:
- 需要自己写sql语句,半自动的ORM框架
- 是jdbc的封装
- 在业务比较复杂系统进行使用
Hibernate:
- 面向对象,全自动的ORM框架
- 根据ORM映射生成不同SQL
- 在业务相对简单的系统进行使用,随着微服务的流行
三、Hibernate的使用
依赖导入:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.6.15.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.22</version>
</dependency>
注解:
@Entity // 作为Hibernate 实体类
@Table(name = "tb_xxxx") // 映射的表名
@Id // 声明主键的配置
@GeneratedValue(strategy = GenerationType.xxx) // 配置主键的生成策略
/**
* GenerationType.IDENTITY:自增,mysql
* GenerationType.SEQUENCE:序列,oracle
* GenerationType.TABLE:jap提供的一种机制,通过一张数据库表的形式帮助我们完成
* GenerationType.AUTO:由程序自动的帮助我们选择主键生成策略
*/
@Column(name = "cus_id") // 配置属性和字段的映射关系
hibernate.cfg.xml配置:
<?xml version="1.0" encoding="UTF‐8"?>
<!DOCTYPE hibernate‐configuration PUBLIC
"‐//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate‐configuration‐3.0.dtd">
<hibernate-configuration>
<!-- 通常,一个session-factory节点代表一个数据库 -->
<session-factory>
<!-- 配置数据库连接信息 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/xxx</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<!-- 在日志中显示hibernate运行时候执行的sql语句 默认false-->
<property name="hibernate.show_sql">true</property>
<!-- 格式化sql 默认false-->
<property name="hibernate.format_sql">true</property>
<!-- 表生成策略
默认none 不自动生成
update 如果没有表会创建,有会检查更新
create 创建-->
<property name="hibernate.hbm2ddl.auto">create</property>
<!--根据不同的方言生成符合当前数据库语法的sql-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!--加载映射(通过xml映射)-->
<mapping resource="zhongfucheng/domain/User.hbm.xml"/>
<!--加载映射(通过注解映射)-->
<mapping class="com.xxx.pojo.xxx"></mapping>
</session-factory>
</hibernate-configuration>
方法测试:
public class HibernateTest {
// Session工厂 Session:数据库会话 代码和数据库的一个桥梁
private SessionFactory sf;
@Before
public void init() {
StandardServiceRegistry registry = new StandardServiceRegistryBuilder().configure("/hibernate.cfg.xml").build();
//根据服务注册类创建一个元数据资源集,同时构建元数据并生成应用一般唯一的的session工厂
sf = new MetadataSources(registry).buildMetadata().buildSessionFactory();
}
@Test
public void testC(){
// 创建Session
try(Session session = sf.openSession()){
// 开启事务
Transaction tx = session.beginTransaction();
// 创建实例
Customer customer = new Customer();
customer.setCustName("TT");
// 插入
session.save(customer);
// 提交事务
tx.commit();
}
}
@Test
public void testR(){
try(Session session = sf.openSession()){
Transaction tx = session.beginTransaction();
// 立即查询
Customer customer = session.find(Customer.class, 1L);
System.out.println("=====================");
System.out.println(customer);
tx.commit();
}
}
@Test
public void testR_lazy(){
try(Session session = sf.openSession()){
Transaction tx = session.beginTransaction();
// 懒加载,会在调用改实例时才去查询
Customer customer = session.load(Customer.class, 1L);
System.out.println("=====================");
System.out.println(customer);
tx.commit();
}
}
@Test
public void testU(){
try(Session session = sf.openSession()){
Transaction tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCustId(1L);
customer.setCustName("TT");
// 更新
session.update(customer);
tx.commit();
}
}
@Test
public void testD(){
try(Session session = sf.openSession()){
Transaction tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCustId(2L);
// 删除
session.remove(customer);
tx.commit();
}
}
@Test
public void testR_HQL(){
try(Session session = sf.openSession()){
Transaction tx = session.beginTransaction();
// 这里可以不用写select Customer为实例类名,不是表名;custId为实例类属性,不是表字段
String hql=" FROM Customer where custId=:id";
List<Customer> resultList = session.createQuery(hql, Customer.class)
//设置hql中的id值
.setParameter("id",1L)
// 执行查询并返回结果集
.getResultList();
System.out.println(resultList);
tx.commit();
}
}
}
注:如果单独使用hibernate的API来进行持久化操作,则不能随意切换其他ORM框架
四、JPA规范的使用
xml配置:
添加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:持久化单元名称
transaction‐type:事务管理的方式
JTA:分布式事务管理
RESOURCE_LOCAL:本地事务管理
‐‐>
<persistence‐unit name="hibernateJPA" transaction‐type="RESOURCE_LOCAL">
<!‐‐jpa的实现方式,可以有多种实现方式,比如hibernate、openjpa等 ‐‐>
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!‐‐ 需要进行ORM的POJO类 ‐‐>
<class>com.pojo.Customer</class>
<!‐‐可选配置:配置jpa实现方的配置信息‐‐>
<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="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/springdata_jpa?serverTimezone=UTC"/>
<!‐‐配置jpa实现方(hibernate)的配置信息
显示sql:false|true(默认false)
自动创建数据库表 : hibernate.hbm2ddl.auto
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
none :不会创建表
‐‐>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
</properties>
</persistence‐unit>
</persistence>
方法测试
public class JpaTest {
//相当于hibernate中的SessionFactory
EntityManagerFactory factory;
@Before
public void before(){
factory= Persistence.createEntityManagerFactory("hibernateJPA");
}
@Test
public void testC(){
//相当于hibernate中的Session
EntityManager em = factory.createEntityManager();
//开启事务
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = new Customer();
customer.setCustName("张三");
//插入操作
em.persist(customer);
//提交事务
tx.commit();
}
// 立即查询
@Test
public void testR(){
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = em.find(Customer.class, 1L);
System.out.println("========================");
System.out.println(customer);
tx.commit();
}
// 延迟查询
@Test
public void testR_lazy(){
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = em.getReference(Customer.class, 1L);
System.out.println("========================");
System.out.println(customer);
tx.commit();
}
@Test
public void testU(){
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = new Customer();
customer.setCustId(5L);
customer.setCustName("王五");
/*
* 如果指定了主键:
* 更新: 先查询 看是否有变化
* 如果有变化 更新
* 如果没有变化就不更新
* 如果没有指定了主键:
* 插入
*/
em.merge(customer);
tx.commit();
}
@Test
public void testU_JPQL(){
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//使用jpql语句来更新数据
String jpql="UPDATE Customer set custName=:name where custId=:id";
em.createQuery(jpql)
.setParameter("name","李四")
.setParameter("id",5L)
.executeUpdate();
tx.commit();
}
@Test
public void testU_SQL(){
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//使用sql语句来更新数据
String sql="UPDATE tb_customer set cust_name=:name where id=:id";
em.createNativeQuery(sql)
.setParameter("name","王五")
.setParameter("id",5L)
.executeUpdate();
tx.commit();
}
@Test
public void testD(){
EntityManager em = factory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Customer customer = em.find(Customer.class,5L);
//如果这里的customer是new出来的,customer将会处于游离状态,删除操作会产生异常,
em.remove(customer);
tx.commit();
}
}
JPA的对象4种状态:
- 临时状态:刚创建出来,∙没有与entityManager发生关系,没有被持久化,不处于entityManager中的对象
- 持久状态:∙与entityManager发生关系,已经被持久化,您可以把持久化状态当做实实在在的数据库记录。
- 删除状态:执行remove方法,事物提交之前
- 游离状态:游离状态就是提交到数据库后,事务commit后实体的状态,因为事务已经提交了,此时实体的属性任你如何改变,也不会同步到数据库,因为游离是没人管的孩子,不在持久化上下文中。
-
public void persist(Object entity)
- persist方法可以将实例转换为managed(托管)状态。在调用flush()方法或提交事物后,实例将会被插入到数据库中。
对不同状态下的实例A,persist会产生以下操作:
- 如果A是一个new状态的实体,它将会转为managed状态;
- 如果A是一个managed状态的实体,它的状态不会发生任何改变。但是系统仍会在数据库执行INSERT操作;
- 如果A是一个removed(删除)状态的实体,它将会转换为受控状态;
- 如果A是一个detached(分离)状态的实体,该方法会抛出IllegalArgumentException异常,具体异常根据不同的
JPA实现有关。
-
public void merge(Object entity)
- merge方法的主要作用是将用户对一个detached状态实体的修改进行归档,归档后将产生一个新的managed状态对象。
对不同状态下的实例A,merge会产生以下操作:
- 如果A是一个detached状态的实体,该方法会将A的修改提交到数据库,并返回一个新的managed状态的实例A2;
- 如果A是一个new状态的实体,该方法会产生一个根据A产生的managed状态实体A2;
- 如果A是一个managed状态的实体,它的状态不会发生任何改变。但是系统仍会在数据库执行UPDATE操作;
- 如果A是一个removed状态的实体,该方法会抛出IllegalArgumentException异常。
-
public void refresh(Object entity)
- refresh方法可以保证当前的实例与数据库中的实例的内容一致。
对不同状态下的实例A,refresh会产生以下操作:
- 如果A是一个new状态的实例,不会发生任何操作,但有可能会抛出异常,具体情况根据不同JPA实现有关;
- 如果A是一个managed状态的实例,它的属性将会和数据库中的数据同步;
- 如果A是一个removed状态的实例,该方法将会抛出异常: Entity not managed
- 如果A是一个detached状态的实体,该方法将会抛出异常。
-
public void remove(Object entity)
- remove方法可以将实体转换为removed状态,并且在调用flush()方法或提交事物后删除数据库中的数据。
对不同状态下的实例A,remove会产生以下操作:
- 如果A是一个new状态的实例,A的状态不会发生任何改变,但系统仍会在数据库中执行DELETE语句;
- 如果A是一个managed状态的实例,它的状态会转换为removed;
- 如果A是一个removed状态的实例,不会发生任何操作;
- 如果A是一个detached状态的实体,该方法将会抛出异常。