步步深入Spring Data JPA系列(一)

Chapter 01-什么是ORM

Section 01-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

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

image.png

Section 02-如何使用JPA API

客户(Customer)的增删改查

  1. 创建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项目并加入相关依赖

  2. 配置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();
    }
}
复制代码

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()方法执行查询操作

@Test
public 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()方法执行查询操作

@Test
public 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语句,即懒加载,一般都会使用延迟加载的方式

执行删除操作

@Test
public 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();
}
复制代码

执行更新操作

@Test
public 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();
}
复制代码

Section 03-什么是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();
    }
}
复制代码

image.png

倒序查询

@Test
public 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();
}
复制代码

image.png

统计查询

@Test
public 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();
}
复制代码

image.png

分页查询

    @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();
    }
}
复制代码

image.png

条件查询

@Test
public 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();
}
复制代码

image.png

JPQL不支持SELECT *

猜你喜欢

转载自juejin.im/post/7040817945202655245