写点什么

Spring 全家桶之 Spring Data JPA(一)

作者:小白
  • 2022 年 8 月 11 日
    上海
  • 本文字数:8731 字

    阅读完需:约 29 分钟

Spring 全家桶之 Spring Data JPA(一)

一、JDBC Template 是如何操作数据库的

  1. 首先在数据库创建 user 表


DROP TABLE IF EXISTS `user`;CREATE TABLE `user` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `username` varchar(255) NOT NULL,  `password` varchar(255) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
复制代码


  1. 创建实体类


public class User{    private Integer id;    private String username;    private String password;    // 此处省略getter/setter/toString方法}
复制代码


3.在 applicationContext.xml 配置文件中注入 jdbcTemplate 及数据源的配置


<!-- 数据源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>    <property name="url"              value="jdbc:mysql://localhost:3306/test?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true"/>    <property name="username" value="root"/>    <property name="password" value="root"/></bean><!--JdbcTemplate提供数据CRUD的API--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">    <property name="dataSource" ref="dataSource"/></bean>
复制代码


4.在 dao 层创建 UserDao,使用 jdbcTemplate


public class UserDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; }
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; }
public User findById(Integer id){ String sql = "SELECT * FROM user WHERE id=?"; //BeanPropertyRowMapper主要是进行数据库数据和实体对象之间的转换 // queryForObject查询单条数据 User user = jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<Employee>(Employee.class)); return user; }}
复制代码


JdbcTemplate 虽然简化了原生 jdbc 的操作,封装了获取数据库连接,创建 prepareStatment 对象等操作,但是仍然需要在代码中写入 SQL 语句,并对占位符进行赋值操作,只不过使用 preparementStatment.setString 赋值操作改为将参数放入数组中进行和占位符的赋值操作,如果想要连 SQL 语句也封装起来,这样在代码中就不会出现 SQL 语句了,也就更进一步的简化了 JDBC 的操作流程。


  以根据 ID 查询为例,SQL 语句为 SELECT * FROM user WHERE id=?其中表明 user 及主键名称 id 是变化的,其余部分是固定结构,而实体类名称和属性是与数据库表名和字段是一一对应的,因此可以通过实体类名记属性确定要操作的数据库表和字段的名字,从而可以根据实体类的不同拼接出不同的 SQL 语句。ORM 思想的主要目的就是操作实体类就相当于操作数据库表,这就需要建立两个映射关系,实体类和表映射关系,实体类字段和表属性的映射关系,不再关注 SQL 语句实现了 ORM 思想的框架有 Hibernate 及 Mybatis

二、什么是 JPA

   JPA 是一套规范,内部由接口和抽象类组成,Hibernate 就是实现了 JPA 规范的 ORM 框架


JPA 的全称是 Java Persistence API, 即 Java 持久化 API,是 SUN 公司推出的一套基于 ORM 的规范,内部是由一系列的接口和抽象类构成


1. 标准化

JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问 API,这保证了基于 JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。


2. 容器级特性的支持

JPA 框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。


3. 简单方便

JPA 的主要目标之一就是提供更加简单的编程模型:在 JPA 框架下创建实体和创建 Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity 进行注释,JPA 的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA 基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成


4. 查询能力

JPA 的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是 Hibernate HQL 的等价物。JPA 定义了独特的 JPQL(Java Persistence Query Language),JPQL 是 EJB QL 的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。


5. 高级特性

JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。


三、如何使用 JPA API

3.1 - 基本增删改查实现

  1. 创建 Customer 实体类对应的数据库表 customer


CREATE TABLE 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;
复制代码


  1. 创建 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>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
<!-- Mysql and MariaDB --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency></dependencies>
复制代码


  1. 配置 JPA 核心配置文件


<?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="myJpa" transaction-type="RESOURCE_LOCAL">        <!--jpa的实现方式 -->        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--可选配置:配置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/test?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
<!--配置jpa实现方(hibernate)的配置信息 显示sql : false|true 自动创建数据库表 : hibernate.hbm2ddl.auto create : 程序运行时创建数据库表(如果有表,先删除表再创建) update :程序运行时创建表(如果有表,不会创建表) none :不会创建表
--> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.hbm2ddl.auto" value="update" /> </properties> </persistence-unit></persistence>
复制代码


  1. 编写客户实体类,配置实体类和表及类属性和表字段之间的映射关系


/** * strategy表示的是主键生成策略   mysql数据库支持主键自增,可以使用IDENTITY   oracle不支持,要使用SEQUENCE    AUTO表示自动选择主键生成策略 */@Entity //表示是一个实体类@Table(name = "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_level") private String custLevel; @Column(name = "cust_industry") private String custIndustry; @Column(name = "cust_phone") private String custPhone; @Column(name = "cust_address") private String custAddress; // 此处省略了getter/setter/toString方法}
复制代码


  1. 在 test 包中创建类 CustomerDaoTest,使用 Junit 进行 JPA 测试


public class CustomerDaoTest {
@Test public void testInsert(){ //1.加载配置文件创建工厂类 EntityManagerFactory managerFactory = Persistence.createEntityManagerFactory("myJpa"); //2.通过工厂类获取实例管理器 EntityManager entityManager = managerFactory.createEntityManager(); //3.获取事务对象,开启事务 EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); Customer customer = new Customer(); customer.setCustName("Tony Stark"); customer.setCustSource("FB"); customer.setCustLevel("VIP"); customer.setCustIndustry("Military Industry"); customer.setCustPhone(null); customer.setCustAddress("NY"); //4.增删改查操作 entityManager.persist(customer); //5.提交/回滚事务 transaction.commit(); //6.关闭资源 entityManager.close(); managerFactory.close(); }}
复制代码

3.2 - JPA 中的 API 对象

Persistence

作用: 根据持久化单元名称创建实体管理器工厂即 EntityManagerFactory。

方法: createEntityManagerFactory,静态方法,参数为持久化单元名称,返回 EntityManagerFactory

EntityManagerFactory

作用:创建 EntityManager 对象

方法:createEntityManger, 返回 EntityManager 对象

特点:EntityManagerFactory 的创建过程比较浪费资源,可以在静态代码块内创建 EntityManagerFactory

  • 内部维护了数据库连接信息

  • 内部维护了缓存信息

  • 内部维护了所有的实体类管理对象

  • 可以根据配置选在创建或者不创建实体类对应的数据库表

EntityManager

作用:实体类管理器,关于表的操作都在该类上

方法:

  • beginTrabsaction:创建事务

  • presist:保存

  • merge:更新

  • remove:删除

  • find/getRefrence:根据 id 查询

Transaction

作用:事务控制

方法:

  • begin:开启事务

  • commit:提交事务,更新操作一定要提交事务

  • rollback:回滚

抽取工具类 JPAUtils 管理工厂类

public class JPAUtils {
private static EntityManagerFactory managerFactory;
static { managerFactory = Persistence.createEntityManagerFactory("myJpa"); }
//获取EntityManager public static EntityManager getEntityManager(){ return managerFactory.createEntityManager(); }}
复制代码

修改 CustomerDao,使用 JPAUtils

public class CustomerDaoTest {
@Test public void testInsert(){ //1.加载配置文件创建工厂类 // EntityManagerFactory managerFactory = Persistence.createEntityManagerFactory("myJpa"); //2.通过工厂类获取实例管理器 EntityManager entityManager = JPAUtils.getEntityManager(); //3.获取事务对象,开启事务 EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); Customer customer = new Customer(); customer.setCustName("Tony Stark"); customer.setCustSource("FB"); customer.setCustLevel("VIP"); customer.setCustIndustry("Military Industry"); customer.setCustPhone(null); customer.setCustAddress("NY"); //4.增删改查操作 entityManager.persist(customer); //5.提交/回滚事务 transaction.commit(); //6.关闭资源 entityManager.close(); // managerFactory.close(); }}
复制代码

在 CustomerDaoTest 中使用 find()方法执行查询操作

@Testpublic void testFindById(){    EntityManager entityManager = JPAUtils.getEntityManager();    EntityTransaction transaction = entityManager.getTransaction();    transaction.begin();    Customer customer = entityManager.find(Customer.class, 1l);    System.out.println(customer);    transaction.commit();    entityManager.close();}
复制代码


执行的 SQL: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 customer customer0_ where customer0_.cust_id=?

输出结果:Customer{custId=1, custName='Tony Stark', custSource='FB', custLevel='VIP', custIndustry='Military Industry', custPhone='null', custAddress='NY'}

在 CustomerDaoTest 中使用 getReference()方法执行查询操作

@Testpublic void testGetReferenceById(){    EntityManager entityManager = JPAUtils.getEntityManager();    EntityTransaction transaction = entityManager.getTransaction();    transaction.begin();    Customer customer = entityManager.getReference(Customer.class, 1l);    System.out.println(customer);    transaction.commit();    entityManager.close();}
复制代码


  find()是在执行完 entityManager.find()后返回 Customer 对象,并在控制台打印 SQL 语句,即立即加载


  getReference()获取的是动态代理对象,并且方法调用时不会立即发送 SQL 语句,即什么时候用什么时候执行 SQL 语句,即懒加载,一般都会使用延迟加载的方式

执行删除操作

@Testpublic void testDelete(){    EntityManager entityManager = JPAUtils.getEntityManager();    EntityTransaction transaction = entityManager.getTransaction();    transaction.begin();    Customer customer = entityManager.getReference(Customer.class, 1l);    entityManager.remove(customer);    System.out.println(customer);    transaction.commit();    entityManager.close();}
复制代码

执行更新操作

@Testpublic void testMerge(){    EntityManager entityManager = JPAUtils.getEntityManager();    EntityTransaction transaction = entityManager.getTransaction();    transaction.begin();    Customer customer = entityManager.getReference(Customer.class, 1l);    customer.setCustName("IronMan Tony Stark");    entityManager.merge(customer);    System.out.println(customer);    transaction.commit();    entityManager.close();}
复制代码

四、JPQL 查询

JPQL 全称 Java Persistence Query Language 基于首次在 EJB2.0 中引入的 EJB 查询语言(EJB QL),Java 持久化查询语言(JPQL)是一种可移植的查询语言,旨在以面向对象表达式语言的表达式,将 SQL 语法和简单查询语义绑定在一起·使用这种语言编写的查询是可移植的,可以被编译成所有主流数据库服务器上的 SQL。


其特征与原生 SQL 语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。

查询全部

在 test package 中新建测试类 JPQLTest


public class JPQLTest {
@Test public void testFindAll(){ EntityManager entityManager = JPAUtils.getEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); String jpql = "FROM Customer"; Query query = entityManager.createQuery(jpql); // 发送查询封装结果 List resultList = query.getResultList(); for (Object o : resultList) { System.out.println(o); } transaction.commit(); entityManager.close(); }}
复制代码


倒序查询

@Testpublic void testFindAllOrderById(){    EntityManager entityManager = JPAUtils.getEntityManager();    EntityTransaction transaction = entityManager.getTransaction();    transaction.begin();    String jpql = "FROM Customer ORDER BY custId DESC";    Query query = entityManager.createQuery(jpql);    // 发送查询封装结果    List resultList = query.getResultList();    for (Object o : resultList) {        System.out.println(o);    }    transaction.commit();    entityManager.close();}
复制代码


统计查询

@Testpublic void testCount(){    EntityManager entityManager = JPAUtils.getEntityManager();    EntityTransaction transaction = entityManager.getTransaction();    transaction.begin();    String jpql = "SELECT COUNT(custId) FROM Customer";    Query query = entityManager.createQuery(jpql);    // 发送查询封装结果    Object resultList = query.getSingleResult();    System.out.println(resultList.toString());    transaction.commit();    entityManager.close();}
复制代码


分页查询

    @Test    public void testPaged(){        EntityManager entityManager = JPAUtils.getEntityManager();        EntityTransaction transaction = entityManager.getTransaction();        transaction.begin();        //查询全部        String jpql = "FROM Customer";        Query query = entityManager.createQuery(jpql);        // 对分页参数进行赋值        //起始索引        query.setFirstResult(1);        //每页查询的个数        query.setMaxResults(1);        // 发送查询封装结果        List resultList = query.getResultList();        for (Object o : resultList) {            System.out.println(o);        }        transaction.commit();        entityManager.close();    }}
复制代码


条件查询

@Testpublic void testCondition(){    EntityManager entityManager = JPAUtils.getEntityManager();    EntityTransaction transaction = entityManager.getTransaction();    transaction.begin();    //查询全部    String jpql = "FROM Customer WHERE custName LIKE ?";    Query query = entityManager.createQuery(jpql);    query.setParameter(1,"Peter%");    // 发送查询封装结果    List resultList = query.getResultList();    for (Object o : resultList) {        System.out.println(o);    }    transaction.commit();    entityManager.close();}
复制代码



JPQL 不支持 ”SELECT * “ 语句

发布于: 刚刚阅读数: 2
用户头像

小白

关注

QA 2019.08.05 加入

微信号JingnanSJ或者公众号RiemannHypo获取异步和图灵系列书籍

评论

发布
暂无评论
Spring 全家桶之 Spring Data JPA(一)_8月月更_小白_InfoQ写作社区