简述普通 Java 与 Spring 工程中 hibernate 的使用与工作原理

Hibernate 简单使用

1.在 Maven 项目中使用 Hibernate,这里省略数据库驱动与数据源的依赖坐标。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>5.4.2.Final</version>
</dependency>

2.创建配置文件 src/main/resources/META-INF/persistence.xml。

<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://java.sun.com/xml/ns/persistence"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
        <description>jpa入门案例演示</description>

        <class>cn.smbms.entity.Product</class>

        <properties>
<!--            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>-->
<!--            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://127.0.0.1:3306/smbms"/>-->
            <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://127.0.0.1:5432/smbms"/>
            <property name="javax.persistence.jdbc.user" value="postgres"/>
            <property name="javax.persistence.jdbc.password" value="123456"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="none"/>
        </properties>
    </persistence-unit>
</persistence>

3.配置与表映射的 Java Entity。

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table
public class Product implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer productId;
    
    @Column
    private String productName;
    
    
    public Integer getProductId() {
        return productId;
    }
    
    public void setProductId(Integer productId) {
        this.productId = productId;
    }
    
    public String getProductName() {
        return productName;
    }
    
    public void setProductName(String productName) {
        this.productName = productName;
    }
}

4.修改 persistence.xml hibernate.hbm2ddl.auto=create 设置 Hibernate 根据 Entity 配置自动建表,注意表结构创建好之后一般后续都修改 hibernate.hbm2ddl.auto 为 none。

<property name="hibernate.hbm2ddl.auto" value="create"/>

5.使用 Hibernate 进行 CRUD 测试。
插入:

    @Test
    public void test_01() {
    	// 根据 persistence.xml 中 persistence-unit=myJpa 配置创建 EntityManager 实例,
    	// 我们通过该 entityManager 操作数据库
        EntityManager entityManager = Persistence.createEntityManagerFactory("myJpa").createEntityManager();
        EntityTransaction tx = null;
        try {
            tx = entityManager.getTransaction();
            tx.begin();
            Product p = new Product();
            p.setProductName("电冰箱");
            entityManager.persist(p);
//            int i = 1 / 0;
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        } finally {
            entityManager.close();
        }
    }

观察控制台我们发现 Hibernate 不仅创建了表结构,还插入了数据:
在这里插入图片描述
执行结果:
在这里插入图片描述

后续演示部分代码省略 EntityManager 实例的创建。

查,findById:

entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
Product product = entityManager.find(Product.class, 1);
logger.info("Product=>id:{},name:{}", product.getProductId(), product.getProductName());
entityManager.getTransaction().commit();
entityManager.close();

查,findAll:

entityManagerFactory = Persistence.createEntityManagerFactory("myJpa");
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
List<Product> list = entityManager.createQuery("from Product", Product.class).getResultList();
list.forEach(x -> logger.info("P=>id:{},name:{}", x.getProductId(), x.getProductName()));
entityManager.getTransaction().commit();
entityManager.close();

改:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
	// 封装了对 Persistence.createEntityManagerFactory("myJpa") 的调用
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    Product p = new Product();
    p.setProductId(3);
    p.setProductName("电冰箱");
    //修改方法
    entityManager.merge(p);
    //  int i = 1/0;
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);  // 判空后调用 tx 的 rollback 方法
} finally {
    JPAUtil.close(entityManager); // 判空后调用 entityManager 的 close 方法
}

改(Java 对象与数据库数据行关联,对象的修改直接同步到数据库):

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    Product p = entityManager.find(Product.class, 3);//p从这之后就会变成持久化对象
    p.setProductName("空调");
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

删:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    Product p = entityManager.find(Product.class, 3);//p从这之后就会变成持久化对象
    entityManager.remove(p);
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,立即加载:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    //find()为立即加载 SQL语句 get()
    Product p = entityManager.find(Product.class, 2);//p从这之后就会变成持久化对象
    //没有使用p对象
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,延迟加载:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    //getReference为延迟加载 SQL语句 load()
    //返回来的是一个 代理模式生成的代理对象 该代理对象中只有主键 其他的并没有
    Product p = entityManager.getReference(Product.class, 1);//p从这之后就会变成持久化对象
    //使用主键
    System.out.println(p.getProductId());
    Thread.sleep(1000);     // sleep 之前 SQL 并未执行
    System.out.println(p.getProductName());
    //没有使用p对象
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,缓存:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    //find()为立即加载 SQL语句 get()
    Product p = entityManager.find(Product.class, 1);//p从这之后就会变成持久化对象
    Product p2 = entityManager.find(Product.class, 1);//p从这之后就会变成持久化对象
    System.out.println(p == p2);// true
    //没有使用p对象
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,JPQL:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    String jpql = "from Product";
    List<Product> resultList = entityManager.createQuery(jpql, Product.class).getResultList();
    resultList.forEach(x -> logger.info("P=>id:{},name:{}", x.getProductId(), x.getProductName()));
    //没有使用p对象
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,JPSQL 模糊查询(参数位置):

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    // 根据产品名 模糊查询产品信息 使用参数位置绑定的方式 传递参数
    // 参数命名方式
    String jpql = "from Product where productName like concat('%',?0,'%')";
    List<Product> resultList = entityManager.createQuery(jpql, Product.class)
                    .setParameter(0, "手").getResultList();
    resultList.forEach(x -> logger.info("P=>id:{},name:{}", x.getProductId(), x.getProductName()));
    tx.commit();            // 没有使用 p 对象
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,JPSQL 模糊查询(参数别名):

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    // 根据产品名 模糊查询产品信息 使用参数命名方式 绑定参数
    String jpql = "from Product where productName like concat('%',:name,'%')";
    List<Product> resultList = entityManager.createQuery(jpql, Product.class)
            .setParameter("name", "手").getResultList();
    resultList.forEach(x -> logger.info("P=>id:{},name:{}", x.getProductId(), x.getProductName()));
    tx.commit();            // 没有使用p对象
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,JPQL 统计:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    //根据产品名 模糊查询产品信息 使用参数命名方式 绑定参数
    String jpql = "select count(*) from Product";
    //uniqueResult():统计查询用的
    Long resultList = ((Long) entityManager.createQuery(jpql).getSingleResult());
      System.out.println(resultList.getClass());//int long
    System.out.println(resultList.intValue());//int long
    //没有使用p对象
    tx.commit();
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

查,JPQL 分页查询:

EntityManager entityManager = null;
EntityTransaction tx = null;
try {
    entityManager = JPAUtil.getEntityManager();
    tx = entityManager.getTransaction();
    tx.begin();
    //根据产品名 模糊查询产品信息 使用参数命名方式 绑定参数
    String jpql = "select count(*) from Product";
    //uniqueResult():统计查询用的
    Long resultList = ((Long) entityManager.createQuery(jpql).getSingleResult());
    int totalCount = resultList.intValue();
    System.out.println("总记录数:" + totalCount);
    int pageNo = 1;
    int pageSize = 2;
    //计算总页数
    int totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
    System.out.println("总页数是:" + totalPage);
    //分页
    String jpqlPage = "from Product order by productId desc";
    List<Product> productList = entityManager.createQuery(jpqlPage, Product.class)
            .setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).getResultList();
    for (Product product : productList) {
        System.out.println(product);
    }
    tx.commit();            // 没有使用p对象
} catch (Exception e) {
    e.printStackTrace();
    JPAUtil.rollback(tx);
} finally {
    JPAUtil.close(entityManager);
}

EntityManager接口详解

参考:https://blog.csdn.net/J080624/article/details/78751411

EntityManger && PersistenceContext 接口详解

参考:https://www.jianshu.com/p/091360c47e6b

Hibernate 的工作原理

1.在我们给项目中引入 hibernate-entitymanager 包后,classpath 下将多出一个与 Hibernate 有关的 Java SPI 文件,其指向了 Java javax.persistence.spi.PersistenceProvider 接口的 java persistence 规范接口的实现 org.hibernate.jpa.HibernatePersistenceProvider。
在这里插入图片描述
2.我们回顾我们 EntityManager 实例的创建: EntityManager entityManager = Persistence.createEntityManagerFactory("myJpa").createEntityManager(),其原理是调用 javax.persistence.Persistence 的静态方法 createEntityManagerFactory。
在这里插入图片描述

createEntityManagerFactory 通过 ServiceLoader.load 加载 Classpath 下 hibernate-core-5.4.2.Final.jar 包中的 SPI 文件,最终拿到 Hibernate 实现的 java persistence org.hibernate.jpa.HibernatePersistenceProvider。

3.HibernatePersistenceProvider 可对项目中 src/main/resources 下 META-INF/persistence.xml 的 Hibernate 配置文件的解析。其解析 persistence-unit 下对 @Entity 与数据库连接配置,并完成 Java Entity 与数据库表之间的映射。最终构造出实例 EntityManager 供我们在 Java 程序中对数据库进行全自动的 orm 操作。

Hibernate 遇上 Spring

当我们想要在 Spring 项目中使用 Hibernate 时,可以考虑 Spring 的 Spring Data Jpa 扩展。

Spring Data Jpa 基于 Hibernate 访问数据库,是简化 JPA 开发的框架,我们只需要按照约定写 DAO 层即可通过方法名称约定生成 JPA 查询,最终以 Spring 的方式来使用 JPA。

Spring Data Jpa 也包括一些额外的功能:分页、排序、复杂查询等。

项目依赖
<!--  Spring IoC 支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
</dependency>
<!--  Spring AOP 支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>
<!--  Spring 数据库连接池支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
</dependency>
<!--  Spring 对 Hibernate 的支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
</dependency>
<!--  Spring 对 Jpa 的支持 -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
</dependency>
<!-- 数据库连接池 -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-c3p0</artifactId>
    <version>${hibernate.version}</version>
</dependency>
<!-- hibernate orm -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>${hibernate.version}</version>
</dependency>
<!-- postgresql 数据库驱动 -->
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.5.0</version>
</dependency>
测试 Spring Data Jpa
public interface ProductDao extends JpaRepository<Product, Integer>, JpaSpecificationExecutor<Product> {
}
@Entity
@Table
@Data
public class Product implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer productId;
    @Column
    private String productName;
}

c3p0 数据库连接池配置:

c3p0.minPoolSize=5
c3p0.maxPoolSize=20
c3p0.idleConnectionTestPeriod=60
c3p0.maxIdleTime=120
c3p0.acquireIncrement=2
c3p0.initialPoolSize=10
c3p0.jdbcUrl=jdbc:postgresql://127.0.0.1:5432/smbms
c3p0.driverClass=org.postgresql.Driver
c3p0.user=postgres
c3p0.password=123456

Spring 配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd
		http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<!--c3p0的数据源-->
	<context:property-placeholder location="classpath:c3p0.properties"/>
	<!--Spring整合hibernate的第二种方式:定义c3p0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close" >
		<property name="user" value="${c3p0.user}"/>
		<property name="password" value="${c3p0.password}"/>
		<property name="driverClass" value="${c3p0.driverClass}"/>
		<property name="jdbcUrl" value="${c3p0.jdbcUrl}"/>
		<!--minPoolSize :最小连接数(default 3)-->
		<property name="minPoolSize" value="${c3p0.minPoolSize}"/>
		<!--maxPoolSize :最大连接数(default 15)-->
		<property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
		<!--initialPoolSize : 初始连接数(default 3)-->
		<property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
		<!--maxIdleTime :最大闲置时间, 未使用的连接在被丢弃之前在连接池存活时间,单位为秒(default 0, 永久存活)-->
		<property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
		<!--acquireIncrement : 获取连接数量, 连接不足时,c3p0尝试一次获取新连接的个数(default 3)-->
		<property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
		<!-- 空闲检查时间间隔, 每隔60秒检查连接池里的空闲连接 ,单位是秒-->
		<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
	</bean>

	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="packagesToScan" value="cn.smbms.entity" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="database" value="POSTGRESQL" />
				<property name="generateDdl" value="false" />
				<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQL10Dialect"/>
				<property name="showSql" value="true"/>
			</bean>
		</property>
	</bean>


	<jpa:repositories base-package="cn.smbms.dao" />

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<!--扫描注解-->
	<context:component-scan base-package="cn.smbms"/>
	
</beans>

启动 Spring 应用进行测试:

public class TestProductDao {
    @Test
    public void test_01(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        ProductDao productDao = applicationContext.getBean(ProductDao.class);
        List<Product> all = productDao.findAll();
        all.forEach(System.out::println);
    }
}
Spring Data Jpa 关键配置说明

transactionManager、entityManagerFactory、jpaVendorAdapter、dataSource。

  • com.mchange.v2.c3p0.ComboPooledDataSource:基于C3P0的数据库连接池,对javax.sql.DataSource的实现,用于管理 Spring 应用中数据库连接。
  • org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter:适配数据库。
  • org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean:
    1. 是一个FactoryBean,用于构造 javax.persistence.EntityManagerFactory Bean, 参考 Java 直接使用 Hibernate 用法。
    2. 基于 Spring 的扫描,不需要 persistence.xml 扫描 entity。
    3. 依赖 jpaVendorAdapter 和 dataSource。
  • org.springframework.orm.jpa.JpaTransactionManager:jpa的事务管理器。
  • <jpa:repositories base-package=“cn.smbms.dao” />:扫描并动态代理继承了 JpaRepository 的 dao,并将代理后的 dao 动态注册到 IoC 容器。
关于对 JpaRepository 接口的代理

用户自定义 JpaRepository dao 层接口在容器中的bean 实际上统一映射为一个 JpaRepositoryFactoryBean 对象,使用者对这样一个 bean 进行依赖注入时,会调用其 FactoryBean#getObject 获取真正要注入的 SimpleJpaRepository 代理对象。
在这里插入图片描述

RepositoryFactoryBean 中依赖注入了 JPA entityManager 实例,也就能操作数据库。最终生成的用户自定义接口 JpaRepository 依赖于 JPA 的实现 Hibernate,Spring Data Jpa 的操作数据库的 SQL 的生成和执行也会依靠 Hibernate 来完成。

关于对 jpa:repositories 标签的解析

在这里插入图片描述
主要就是启用了 spring.handlers 对 <jpa:repositories base-package=“cn.smbms.dao” /> 标签进行解析。spring.handlers 文件配置告诉 Spring 该如何来解析你自定义的配置文件。

在这个配置类中,通过 registerBeanDefinitionParser 向 Spring 注册了 RepositoryBeanDefinitionParser 和 AuditingBeanDefinitionParser 与 JPA 有关的 BeanDefinition 解析器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值