Spring Data ~ JPA。

Spring Data ~ JPA。



ORM、Hibernate、JPA 概述 + jpa 基本操作。

orm 思想。

对象关系映射(Object Relational Mapping,简称 ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

操作对象就相当于操作数据库。

自动建立两个映射。

实体类和表的映射关系。
实体类中属性和表中字段的映射关系。
MyBatis 、Hibernate。



jdbc。
// 操作繁琐。
// 占位符赋值麻烦。
CREATE TABLE `new_schema`.`t_user` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(45) NULL,
  `address` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));
package com.geek.jdbc;

import com.geek.domain.User;
import com.mysql.cj.jdbc.Driver;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JdbcDemo {

    // 操作繁琐。
    // 占位符赋值麻烦。

    public static void main(String[] args) throws SQLException, ClassNotFoundException {

        String sql = "insert into t_user (username, address) values (?, ?)";

//        DriverManager.registerDriver(new Driver());
        Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");

        Connection connection = DriverManager.getConnection("jdbc:mysql://192.168.142.135/new_schema?characterEncoding=UTF-8", "root", "root");

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        User user = new User();
        user.setUsername("geek");
        user.setAddress("武汉。");

        preparedStatement.setString(1, user.getUsername());
        preparedStatement.setString(2, user.getAddress());

        int i = preparedStatement.executeUpdate();

        System.out.println("i = " + i);

        preparedStatement.close();
        connection.close();
    }

}

在这里插入图片描述



Hibernate 框架。

Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,它将 POJO 与数据库表建立映射关系,是一个全自动的 orm 框架,hibernate 可以自动生成 SQL 语句,自动执行,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate 可以应用在任何使用 JDBC 的场合,既可以在 Java 的客户端程序使用,也可以在 Servlet/JSP 的 Web 应用中使用,最具革命意义的是,Hibernate 可以在应用 EJB 的 JaveEE 架构中取代 CMP,完成数据持久化的重任。
~ 百度百科。

↓↓↓ 更好。



JPA 规范。

Sun 公司定义的规范(接口和抽象类)。生产厂商实现。

在这里插入图片描述



JPA 基本操作。

增删改查。

搭建环境。
  • database。
CREATE TABLE `new_schema`.`cst_customer` (
  `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)。',
  `cust_name` VARCHAR(32) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL COMMENT '客户名称(公司名称)。',
  `cust_source` VARCHAR(45) NULL COMMENT '客户信息来源。',
  `cust_industry` VARCHAR(45) NULL COMMENT '客户所属行业。',
  `cust_lovel` VARCHAR(45) NULL COMMENT '客户级别。',
  `cust_address` VARCHAR(128) NULL COMMENT '客户地址。',
  `cust_phone` VARCHAR(64) NULL COMMENT '客户联系电话。',
  PRIMARY KEY (`cust_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
  • Maven。

    <dependencies>
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
        <!-- Hibernate 对 JPA 的支持包。-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.11.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-c3p0 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>5.4.11.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>
    </dependencies>

  • 配置文件。

配置到类路径下 META-INF 的文件夹下。
命名:persistence.xml

引入约束。

在这里插入图片描述
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">

</persistence>

<?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>
            <!-- 数据库信息。-->
            <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.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.url"
                      value="jdbc:mysql://192.168.142.135/new_schema?characterEncoding=UTF-8"/>

            <!-- 配置 jpa 实现方(Hibernate)的配置信息。
                显示 sql。~ hibernate.show_sql。
                自动创建数据库表。~ hibernate.hbm2ddl.auto。
                    create      程序运行时创建数据表。(如果有表,先删除表再创建)。
                    update      程序运行时创建表。(如果有表,不会创建表)。
                    none        不会创建表。
            -->
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="create"/>
        </properties>
    </persistence-unit>
</persistence>

实体类。

package com.geek.domain;

import javax.persistence.*;

/**
 * 客户实体类。
 */
@Entity// 声明实体类。
@Table(name = "cst_customer")
public class Customer {

    /*
     * 配置映射关系。
     *      实体类和表的映射关系。
     *          @Entity// 声明实体类。
     *          @Table(name = "cst_customer")
     *          配置实体类和表的映射关系。
     *              name:配置数据库表的名称。
     *      实体类中属性和表中字段的映射关系。
     *          @Id// 主键。
     *          @GeneratedValue。配置主键的生成策略。
     *              strategy = GenerationType.IDENTITY。自增。
     *          @Column。配置属性和字段的映射关系。
     *              (name = "cust_id")。数据库中表字段的名称。
     */

    @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;

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long 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 getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustPhone() {
        return custPhone;
    }

    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }

    public String getCustAddress() {
        return custAddress;
    }

    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "custId=" + custId +
                ", custName='" + custName + '\'' +
                ", custSource='" + custSource + '\'' +
                ", custLevel='" + custLevel + '\'' +
                ", custIndustry='" + custIndustry + '\'' +
                ", custPhone='" + custPhone + '\'' +
                ", custAddress='" + custAddress + '\'' +
                '}';
    }
}



CRUD。
package com.geek.test;

import com.geek.domain.Customer;
import org.junit.Test;

import javax.persistence.*;

public class JpaTest {

    /**
     * 测试 jpa 的保存。
     * <p>
     * 加载配置文件创建工厂(实体管理器工厂)对象。
     * 通过实体管理器工厂获取实体管理器。
     * 获取事务对象,开启事务。
     * 完成增删改查操作。
     * 提交事务。
     * 释放资源。
     */
    @Test
    public void testSave() {
        // 加载配置文件创建工厂(实体管理器工厂)对象。
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJPA");

        // 通过实体管理器工厂获取实体管理器。
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        // 获取事务对象,开启事务。
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();

        // 完成增删改查操作。
        Customer customer = new Customer();
        customer.setCustName("李");
        customer.setCustAddress("武汉");

        // 保存。
        entityManager.persist(customer);

        // 提交事务。
        transaction.commit();

        // 释放资源。
        entityManager.close();
        entityManagerFactory.close();
    }
}

日志文件输出 sql 语句。

log4j:WARN No appenders could be found for logger (org.jboss.logging).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Hibernate: drop table if exists cst_customer
Hibernate: create table cst_customer (cust_id bigint 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)) engine=InnoDB
Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)

此处有 drop table if exists cst_cunstomer 是因为

            <!-- 配置 jpa 实现方(Hibernate)的配置信息。
                显示 sql。~ hibernate.show_sql。
                自动创建数据库表。~ hibernate.hbm2ddl.auto。
                    create      程序运行时创建数据表。(如果有表,先删除表再创建)。
                    update      程序运行时创建表。(如果有表,不会创建表)。
                    none        不会创建表。
            -->


主键生成策略。
     *      实体类中属性和表中字段的映射关系。
     *          @Id// 主键。
     *          @GeneratedValue。配置主键的生成策略。
     *              strategy = GenerationType.IDENTITY。自增。
     *                  底层数据库必须支持自动增长。(MySQL~AutoIncrement)。
     *              strategy = GenerationType.SEQUENCE。序列。
     *                  底层数据库必须支持序列。(Oracle)。
     *              strategy = GenerationType.TABLE。
     *                  jpa 提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增。
     *              strategy = GenerationType.AUTO。
     *                  由程序自动的帮助我们选择主键生成策略。
  • 如果使用 GenerrationType.Sequence,jpa 会创建 2 张表。hibernate_sequences 以记录下一个 id 的值。
Hibernate: drop table if exists cst_customer
Hibernate: drop table if exists hibernate_sequences
Hibernate: create table cst_customer (cust_id bigint 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)) engine=InnoDB
Hibernate: create table hibernate_sequences (sequence_name varchar(255) not null, next_val bigint, primary key (sequence_name)) engine=InnoDB
Hibernate: insert into hibernate_sequences(sequence_name, next_val) values ('default',0)
Hibernate: select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update
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 (?, ?, ?, ?, ?, ?, ?)

  • strategy = GenerationType.AUTO。
    jpa 会选择 SEQUENCE。
Hibernate: drop table if exists cst_customer
Hibernate: drop table if exists hibernate_sequence
Hibernate: create table cst_customer (cust_id bigint 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)) engine=InnoDB
Hibernate: create table hibernate_sequence (next_val bigint) engine=InnoDB
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 (?, ?, ?, ?, ?, ?, ?)

Process finished with exit code 0



JPA 运行原理 + 基本操作。

api 基本介绍。
package com.geek.test;

import com.geek.domain.Customer;
import org.junit.Test;

import javax.persistence.*;
import javax.xml.crypto.dsig.TransformService;

public class JpaTest {

    /**
     * 测试 jpa 的保存。
     * <p>
     * 加载配置文件创建工厂(实体管理器工厂)对象。
     * 通过实体管理器工厂获取实体管理器。
     * 获取事务对象,开启事务。
     * 完成增删改查操作。
     * 提交事务。
     * 释放资源。
     */
    @Test
    public void testSave() {
        // 加载配置文件创建工厂(实体管理器工厂)对象。
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("myJPA");
//            <persistence-unit name="myJPA" transaction-type="RESOURCE_LOCAL">

        // 获取 EntityManager 对象。
        // 通过实体管理器工厂获取实体管理器。
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        // 内部维护了数据库信息。
        // 内部维护了缓存信息。
        // 内部维护了所有的实体管理器对象。
        // 创建 EntityManagerFactory 的过程中会根据配置创建数据库表。
        // ——>
        // EntityManagerFactory 的创建过程比较浪费资源。
        // 线程安全的对象。
        //      多个线程访问同一个 EntityManagerFactory 不会有线程安全问题。
        // ——>
        // 创建一个公共的 EntityManagerFactory 对象。
        // 静态代码块。

//        entityManager.persist();
//        entityManager.merge();
//        entityManager.remove();
//        entityManager.find();
//        entityManager.getReference();// 根据 id 查询,

        // 获取事务对象,开启事务。
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();// 开启事物。

//        transaction.commit();
//        transaction.rollback();

        // 完成增删改查操作。
        Customer customer = new Customer();// 模拟客户。
        customer.setCustName("李");
        customer.setCustAddress("武汉");

        // 保存。
        entityManager.persist(customer);

        // 提交事务。
        transaction.commit();

        // 释放资源。
        entityManager.close();
        entityManagerFactory.close();
    }

}



工具类抽取。
package com.geek.utils;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 * 解决实体管理器工厂的浪费资源和耗时的问题。
 * 通过静态代码块的形式,当程序第一次访问此工具类时,创建一个公共的实体管理器工厂对象。
 */
public class JpaUtils {

    private static EntityManagerFactory entityManagerFactory;

    static {
        // 加载配置文件。创建 EntityManagerFactory。
        entityManagerFactory = Persistence.createEntityManagerFactory("myJPA");
    }


    public static EntityManager getEntityManager() {
        return entityManagerFactory.createEntityManager();
    }

}



根据 id 查询。
  • entityManager.find();

    /**
     * 根据 id 查询。
     */
    @Test
    public void testFindById() {
        // 通过工具类获取 EntityManager。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        // 增删改查。~根据 id 查询。(实体管理器)。
        try {
            Object o = entityManager.find(Class.forName("com.geek.domain.Customer"), 1L);
            System.out.println("o = " + o);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.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=?
o = Customer{custId=1, custName='李', custSource='null', custLevel='null', custIndustry='null', custPhone='null', custAddress='武汉'}

根据日志分析 jpa 执行流程。

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=?
  • entityManager.getReference();

其他代码不变。

Object o = entityManager.find(Class.forName("com.geek.domain.Customer"), 1L);
变为
Object o = entityManager.getReference(Class.forName("com.geek.domain.Customer"), 1L);



延迟加载和立即加载。
entityManager.find();~立即加载。
  • 查询的对象是对象本身。
  • 调用 entityManager.getReference(); 方法立即发送 sql 语句查询数据库。
entityManager.getReference();~延迟加载。
  • 获取的对象是一个动态代理对象。
  • 调用 entityManager.getReference(); 方法不会立即发送 sql 语句查询数据库。
  • 当调用查询结果对象的时候,才会发送查询的 sql 语句。


根据 id 删除。remove。

entityManager.remove(customer);// 参数需要一个 Object。

    /**
     * 根据 id 删除。
     */
    @Test
    public void testRemove() {
        // 通过工具类获取 EntityManager。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        // 增删改查。~根据 id 删除。(实体管理器)。
        Customer customer = entityManager.find(Customer.class, 1L);
        entityManager.remove(customer);// 参数需要一个 Object。

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.close();
    }


改。merge。

    /**
     * 改。
     */
    @Test
    public void testMerge() {
        // 通过工具类获取 EntityManager。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();

        // 增删改查。~改。(实体管理器)。
        Customer customer = entityManager.find(Customer.class, 2L);
        // 更新。
        customer.setCustIndustry("java 开发。");
        entityManager.merge(customer);// 参数需要一个 Object。

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.close();
    }


JPQL。

JPQL,Java Persistence Query Language。

sql 查询的是表和表中的字段。
jpql 查询的是实体类和类中的属性。

创建 query 查询对象。
对参数进行赋值。
查询。并得到返回结果。

package com.geek.test;

import com.geek.utils.JpaUtils;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import java.util.List;

/**
 * 测试 jpql 查询。
 */
public class JPQLTest {

    /**
     * 查询全部。
     * <p>
     * jpql:from com.geek.domain.Customer
     * sql:SELECT * FROM new_schema.cst_customer;
     */
    @Test
    public void testFindAll() {
        // 获取 EntityManager 对象。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        // 查询全部。
        String jpql = "from com.geek.domain.Customer";
        Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。

        // 发送查询。并封装结果集。
        List resultList = query.getResultList();

        for (Object o : resultList) {
            System.out.println("o = " + o);
        }

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.close();
    }

}

Hibernate: select customer0_.cust_id as cust_id1_0_, customer0_.cust_address as cust_add2_0_, customer0_.cust_industry as cust_ind3_0_, customer0_.cust_level as cust_lev4_0_, customer0_.cust_name as cust_nam5_0_, customer0_.cust_phone as cust_pho6_0_, customer0_.cust_source as cust_sou7_0_ from cst_customer customer0_
o = Customer{custId=1, custName='李', custSource='null', custLevel='null', custIndustry='null', custPhone='null', custAddress='武汉'}
o = Customer{custId=2, custName='李', custSource='null', custLevel='null', custIndustry='java 开发。', custPhone='null', custAddress='武汉'}
    /**
     * 排序查询。(倒序)。
     *      sql:SELECT * FROM new_schema.cst_customer ORDER BY cust_id DESC;
     *      jpql:from Customer order by custId desc
     */
    @Test
    public void testOrders() {
        // 获取 EntityManager 对象。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        // 查询全部。
        String jpql = "from Customer order by custId desc";
        Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。

        // 发送查询。并封装结果集。
        List resultList = query.getResultList();

        for (Object o : resultList) {
            System.out.println("o = " + o);
        }

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.close();
    }

    /**
     * 统计总数。
     * sql:SELECT COUNT(cust_id) FROM new_schema.cst_customer;
     * jpql:select count(custId) from Customer
     */
    @Test
    public void testCount() {
        // 获取 EntityManager 对象。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        // 查询总数。
        //  根据 jpql 语句创建 Query 对象。
        String jpql = "select count(custId) from Customer";
        Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。
        // 对象参数赋值。
        // 发送查询。并封装结果。(唯一)。
        Object singleResult = query.getSingleResult();
        System.out.println("singleResult = " + singleResult);

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.close();
    }

    /**
     * 分页查询。
     * sql:SELECT * FROM new_schema.cst_customer limit ?, ?;
     * jpql:from Customer
     */
    @Test
    public void testPage() {
        // 获取 EntityManager 对象。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        // 查询总数。
        //  根据 jpql 语句创建 Query 对象。
        String jpql = "from Customer";
        Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。

        // 对象参数赋值。分页参数。
        // 起始索引。
        query.setFirstResult(0);// limit ?
//        query.setFirstResult(1);// limit ?, ?
        // 每页条数。
        query.setMaxResults(2);

        // 发送查询。并封装结果。(唯一)。
        List resultList = query.getResultList();
        for (Object o : resultList) {
            System.out.println("o = " + o);
        }

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.close();
    }
    /**
     * 条件查询。
     * sql:SELECT * FROM new_schema.cst_customer where cust_name LIKE ?;
     * jpql:from Customer where custName like ?
     */
    @Test
    public void testCondition() {
        // 获取 EntityManager 对象。
        EntityManager entityManager = JpaUtils.getEntityManager();
        // 开启事务。
        EntityTransaction tx = entityManager.getTransaction();
        tx.begin();
        // 查询总数。
        //  根据 jpql 语句创建 Query 对象。
        String jpql = "from Customer where custName like ?";
        Query query = entityManager.createQuery(jpql);// Query 对象才是执行 jpql 的对象。

        // 对象参数赋值。占位符参数。
        // 占位符位置。从 1 开始。
        query.setParameter(1, "李%" );

        // 发送查询。并封装结果。(唯一)。
        List resultList = query.getResultList();
        for (Object o : resultList) {
            System.out.println("o = " + o);
        }

        // 提交事务。
        tx.commit();
        // 释放资源。
        entityManager.close();
    }


Spring Data Jpa。

https://spring.io/projects/spring-data-jpa

Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use data access technologies.

Implementing a data access layer of an application has been cumbersome for quite a while. Too much boilerplate code has to be written to execute simple queries as well as perform pagination, and auditing. Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed. As a developer you write your repository interfaces, including custom finder methods, and Spring will provide the implementation automatically.

Features

  • Sophisticated support to build repositories based on Spring and JPA
  • Support for Querydsl predicates and thus type-safe JPA queries
  • Transparent auditing of domain class
  • Pagination support, dynamic query execution, ability to integrate custom data access code
  • Validation of @Query annotated queries at bootstrap time
  • Support for XML based entity mapping
  • JavaConfig based repository configuration by introducing @EnableJpaRepositories.

在这里插入图片描述
JPA 是一套规范,内部由接口的抽象类组成。

Hibernate 是一套成熟的 ORM 框架,而且 Hibernate 实现了 JPA 规范,所以也可以说 Hibernate 是 JPA 的一种实现方式。我们使用 JPA 的 API 编程,意味着站在更高的角度上看待问题(面向接口编程)。

Spring Data JPA 是 Spring 提供的一套对 JPA 操作更加高级的封装,是 在 JPA 规范下的专门用来进行数据持久化的解决方案。



使用。

创建工程导入坐标。
配置 Spring。
编写实体类(Customer),使用 jpa 注解配置映射关系。
编写一个符合 SpringDataJpa 的 dao 接口层。

  • Maven pom。
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <!-- Spring 对 orm 框架的支持。-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.4.11.Final</version>
        </dependency>

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

        <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.19.Final</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.29</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.29</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.16.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
            <!--<scope>test</scope>-->
        </dependency>


经测试,2.1.16.RELEASE 的 spring-data-jpa 添加此依赖反而报错。
Caused by: java.lang.ClassNotFoundException: javax.el.ELManager
去掉这两个依赖即可。

        <!-- el。使用 Spring Data Jpa 必须引入。-->
        <!-- https://mvnrepository.com/artifact/javax.el/javax.el-api -->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>2.2.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.web/javax.el -->
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.el</artifactId>
            <version>2.2.4</version>
        </dependency>

    </dependencies>
  • 数据库。
CREATE SCHEMA `jpa_geek` DEFAULT CHARACTER SET utf8 ;

CREATE TABLE `jpa_geek`.`cst_customer` (
  `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)。',
  `cust_name` VARCHAR(32) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL COMMENT '客户名称(公司名称)。',
  `cust_source` VARCHAR(45) NULL COMMENT '客户信息来源。',
  `cust_industry` VARCHAR(45) NULL COMMENT '客户所属行业。',
  `cust_level` VARCHAR(45) NULL COMMENT '客户级别。',
  `cust_address` VARCHAR(128) NULL COMMENT '客户地址。',
  `cust_phone` VARCHAR(64) NULL COMMENT '客户联系电话。',
  PRIMARY KEY (`cust_id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
  • 配置文件。
<?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:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://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/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 创建 EntityManagerFactory 对象交给 Spring 容器管理。-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.geek.domain"/>
        <!-- jpa 实现厂家。-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>
        <!-- jpa 的供应商适配器。-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- 自动创建数据库表。-->
                <property name="generateDdl" value="false"/>
                <!-- 指定数据库类型。-->
                <property name="database" value="MYSQL"/>
                <!--数据库方言。支持特有语法。-->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
                <!-- 是否显示 sql 语句。-->
                <property name="showSql" value="true"/>
            </bean>
        </property>

        <!-- jpa 方言。-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
        </property>

    </bean>

    <!-- 数据库连接池。-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value=""/>
        <property name="jdbcUrl" value="jdbc:mysql://192.168.142.135/new_schema?characterEncoding=UTF-8"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- 整合 Spring Data Jpa。-->
    <jpa:repositories base-package="com.geek.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory"/>

    <!-- 配置事务管理器。-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <!-- 声明式事务。-->

    <!-- 注解扫描。-->
    <context:component-scan base-package="com.geek"/>

</beans>

  • 实体类。
package com.geek.domain;

import javax.persistence.*;

/**
 * 实体类和表的映射关系。
 * + @Entity
 * + @Table
 * <p>
 * 类中属性和表中字段的映射关系。
 * + @Id
 * + @GeneratedValue
 * + @Column
 */
@Entity
@Table(name = "cst_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;

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long 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 getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustPhone() {
        return custPhone;
    }

    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }

    public String getCustAddress() {
        return custAddress;
    }

    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "custId=" + custId +
                ", custName='" + custName + '\'' +
                ", custSource='" + custSource + '\'' +
                ", custLevel='" + custLevel + '\'' +
                ", custIndustry='" + custIndustry + '\'' +
                ", custPhone='" + custPhone + '\'' +
                ", custAddress='" + custAddress + '\'' +
                '}';
    }
}

  • dao。
package com.geek.dao;

import com.geek.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 * 只需要编写 dao 层接口,不需要编写 dao 层实现类。
 * <p>
 * dao 规范。
 * + 需要继承两个接口:Jpa
 * + 需要提供相应的泛型。
 * JpaRepository<操作的实体类类型,实体类中主键属性的类型>。
 *      ~ 封装了基本 CRUD 操作。
 * JpaSpecificationExecutor<操作的实体类类型>
 *     ~ 封装了复杂查询(分页...)。
 */
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {

}

  • 测试。
package com.geek.test;

import com.geek.dao.CustomerDao;
import com.geek.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.Optional;

@RunWith(SpringJUnit4ClassRunner.class)// 声明 Spring 提供的单元测试环境。
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CustomerDaoTest {

    @Autowired
    private CustomerDao customerDao;

    @Test
    public void testFindOne() {
//        Customer customer = customerDao.findOne(3L);
        Optional<Customer> customer = customerDao.findById(1L);
        System.out.println("customer = " + customer);
    }

}

log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUnit4ClassRunner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
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 = Optional[Customer{custId=1, custName='李', custSource='null', custLevel='null', custIndustry='null', custPhone='null', custAddress='武汉'}]

Process finished with exit code 0
package com.geek.test;

import com.geek.dao.CustomerDao;
import com.geek.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;
import java.util.Optional;

@RunWith(SpringJUnit4ClassRunner.class)// 声明 Spring 提供的单元测试环境。
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class CustomerDaoTest {

    @Autowired
    private CustomerDao customerDao;

    /**
     * 根据 id 查询。
     */
    @Test
    public void testFindOne() {
//        Customer customer = customerDao.findOne(3L);
        Optional<Customer> customer = customerDao.findById(1L);
        System.out.println("customer = " + customer);
    }

    /**
     * 保存或更新。
     * 根据传递的对象查询是否存在主键。
     * 如果没有 id 主键属性,保存。
     * 如果有 id 主键属性,根据 id 查询数据,更新数据。
     */
    @Test
    public void testSave() {
        Customer customer = new Customer();
        customer.setCustName("Geek 李");
        customer.setCustLevel("vip");
        customerDao.save(customer);
        // Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?)
    }

    @Test
    public void testUpdate() {
        Customer customer = new Customer();
        customer.setCustId(3L);
        customer.setCustName("Geek");
        customer.setCustAddress("武汉");
        customer.setCustLevel("vip");
        customerDao.save(customer);
        // Hibernate: update cst_customer set cust_address=?, cust_industry=?, cust_level=?, cust_name=?, cust_phone=?, cust_source=? where cust_id=?
    }

    /**
     * 删除。
     */
    @Test
    public void testDelete() {
        customerDao.deleteById(6L);
        // 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=?
        //Hibernate: delete from cst_customer where cust_id=?
    }

    /**
     * 查询所有。
     */
    @Test
    public void testFindAll() {
        List<Customer> all = customerDao.findAll();
        all.forEach(System.out::println);
    }
}



多表操作 + 复杂查询。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lyfGeek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值