天天看点

Spring Data Jpa的使用:入门知识Spring Data JPA

Spring Data JPA

1、入门案例

数据库脚本

案例演示,所需的数据库表

/*创建客户表*/
CREATE TABLE cst_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=94 DEFAULT CHARSET=utf8;

/*创建联系人表*/
CREATE TABLE cst_linkman (
  lkm_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
  lkm_name varchar(16) DEFAULT NULL COMMENT '联系人姓名',
  lkm_gender char(1) DEFAULT NULL COMMENT '联系人性别',
  lkm_phone varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
  lkm_mobile varchar(16) DEFAULT NULL COMMENT '联系人手机',
  lkm_email varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
  lkm_position varchar(16) DEFAULT NULL COMMENT '联系人职位',
  lkm_memo varchar(512) DEFAULT NULL COMMENT '联系人备注',
  lkm_cust_id bigint(32) NOT NULL COMMENT '客户id(外键)',
  PRIMARY KEY (`lkm_id`),
  KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
  CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
           

项目的创建

1、创建一个springboot项目,然后引入web、Spring Data JPA、Mysql Driver依赖【项目存放的路径径:D:\springboot\jpaWanZheng\demo1】

2、配置pom.xml文件

<!--1、需要指定mysql的驱动版本,默认是mysql8-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.44</version>
        </dependency>
        <!--2、配置数据库连接池【一般我们都使用druid】【在配置多数据源的时候会用到】[这个依赖是专门为springboot定做的数据库连接池]-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
           

3、创建application.properties文件

#需要配置4个属性
#spring中使用jdbcTemplate,需要配置数据源【就不用配置驱动了】
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://127.0.0.1:3307/datajpa?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
#jpa额外的配置
#为了调试方便,我希望把生成的SQL语句打印出来【因为我一会儿创建的过程中,不用创建表了,不用写SQL了---而sql,它会根据我写的代码自动生成生sql】
spring.jpa.show-sql=true
#使用哪种数据库
spring.jpa.database=mysql
spring.jpa.database-platform=mysql
#一会儿,我只需要创建实体类,而框架会根据我的实体类,自动的把表创建出来【第一次启动的时候,给你创建一个表,但不能每次启动,创建表,否则原有的数据都丢失了】
#描述每次启动的时候,你要怎么办:一般选update【表示每次启动的时候,如果表已经存在了,那么就检查一下是否跟实体类对应;如果不一样,根据实体类更新表;一样,就直接使用】
#如果表不存在,会自动的创建出表
spring.jpa.hibernate.ddl-auto=update
#指定使用默认创建表使用的存储引擎为innodb
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
           

4、创建实体类

Customer

package org.javaboy.demo1.entity;

import javax.persistence.*;

@Entity(name = "cst_customer") //指定表的名称  默认情况下,实例的名字,就是表的名字
public class Customer {
    @Id //在jpa中,强制要求,每个实体类中必须有主键;否则表创建不出来
    @GeneratedValue(strategy = GenerationType.IDENTITY) //设置id是自增长的;并指定自增长的策略,一般是IDENTITY
    @Column(name="cust_id") //如果实体类中,普通属性与表的字段,有极个别的不对应,可通过@Column来指定
    private Long custId;
    @Column(name="cust_address")
    private String custAddress;
    @Column(name="cust_industry")
    private String custIndustry;
    @Column(name="cust_level")
    private String custLevel;
    @Column(name="cust_name")
    private String custName;
    @Column(name="cust_phone")
    private String custPhone;
    @Column(name="cust_source")
    private String custSource;

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

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustAddress() {
        return custAddress;
    }

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

    public String getCustIndustry() {
        return custIndustry;
    }

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

    public String getCustLevel() {
        return custLevel;
    }

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

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustPhone() {
        return custPhone;
    }

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

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }
}
           

5、创建Dao

CustomerDao

package org.javaboy.demo1.dao;

import org.javaboy.demo1.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 *  JpaRepository<Customer,Long>  作用:封装了基本的crud操作
 *        第一个参数:CustomerDao要操作的实体类,是谁
 *        第二个参数:实体类的id,的数据类型
 *  JpaSpecificationExecutor<Customer>  参数1:指定CustomerDao要操作的实体类
 *         JpaSpecificationExecutor作用: 封装了复杂查询【比如:分页查询】
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
}
           

6、使用单元测试,来进行测试

package org.javaboy.demo1;

import org.javaboy.demo1.dao.CustomerDao;
import org.javaboy.demo1.entity.Customer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;

@SpringBootTest
class Demo1ApplicationTests {
    @Autowired
    private CustomerDao customerDao;

    /**
     * 查询:根据id进行查询对于的记录
     */
    @Test
    void contextLoads() {
        Optional<Customer> customer = customerDao.findById(94l);
        System.out.println(customer.get());
    }
    /**
     * save : 保存或者更新--------插入操作
     *      根据传递的对象是否存在主键id,
     *          如果没有id主键属性:保存
     *          如果存在id主键属性,根据id查询数据,更新数据
     */
    @Test
    public void testSave() {
        Customer customer  = new Customer();
        customer.setCustName("黑马程序员");
        customer.setCustLevel("vip");
        customer.setCustIndustry("it教育");
        customerDao.save(customer);
    }

    /**
     * 更新操作-------前提,数据库中,存在要更新的记录
     */
    @Test
    public void testUpdate() {
        Customer customer  = new Customer();
        customer.setCustId(94l);
        customer.setCustName("黑马程序员很厉害");
        customerDao.save(customer);
    }

    /**
     * 删除操作---根据id,进行删除
     */
    @Test
    public void testDelete () {
        customerDao.deleteById(94l);
    }


    /**
     * 查询所有记录
     */
    @Test
    public void testFindAll() {
        List<Customer> list = customerDao.findAll();
        for(Customer customer : list) {
            System.out.println(customer);
        }
    }

    /**
     * 统计查询:查询表中,所有的记录
     */
    @Test
    public void testCount() {
        long count = customerDao.count();//查询表中的,所有记录数
        System.out.println(count);
    }

    /**
     * 判断,指定id,是否存在
     */
    @Test
    public void  testExists() {
        boolean exists = customerDao.existsById(4l);
        System.out.println("id为4的客户 是否存在:"+exists);
    }


    /**
     * 根据id从数据库查询:
     *           方法名声明规则:前缀 get find read   只有这3种【习惯用find,因为他有提示】
     */
    @Test
    public void  testGetOne() {
        Optional<Customer> customer = customerDao.findById(95l);
        if (customer.isPresent()){//判断,是否能够查询到该记录,如果查询到,就进入if代码块中
            System.out.println(customer.get());
        }

    }

}
           

2、jpql的学习

jpql:它跟sql类似,但是有一些区别

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

//1、查询所有数据
sql: select * from cst_customer;
jpql: from cn.itcast.domain.Customer;    格式:from  全类名或类名;

//2、倒序查询所有数据
sql:select * from cst_customer order by cust_id desc;
jpql:from cn.itcast.domain.Customer order by custId;  //要排序的字段,是从Customer类中,找它对应的属性

//3、统计查询
sql:select count(cust_id) from cst_customer;
jpql: select count(custId)from Customer;

//4、分页查询
sql:select * from  cst_customer limit ?,?;
jpql:from Customer     分页查询参数,通过jpa提供的api,进行赋值

//5、模糊查询
sql:select * from cst_customer where cust_name like ?
jpql: from Customer where custName like ?    参数具体的值,通过jpa提供的api,进行赋值
           

书写jpql的注意事项:

  • 1、jpql不能写select * ,但是对于 select 字段1,字段2 是能够识别的
  • 2、先写sql,然后根据sql,写对应的jpql------------直接把表名 转为实体类的名称,把表的字段 改成 实体类中对应的属性名称

3、JPA中使用jpql来实现CRUD【复杂sql,使用这种】

使用jpql的步骤:

  • 1、在Dao中,添加方法
  • 2、在新添加的方法上,通过注解的方式,来配置jpql查询语句

注意事项:

  • 如果要使用jpql,并且想使用简写的方式,来写jpql中 类名,需要使用@Table(name = “cst_customer”) 来指定,实体类,对应的表------否则,你只能写全类名 【其他写法报错】

1、创建实体类------------------------其他项目的创建,application.properties的配置,看入门案例中的配置

Customer

package org.javaboy.demo1.entity;

import javax.persistence.*;

@Entity
@Table(name = "cst_customer") //指定表的名称  默认情况下,实例的名字,就是表的名字--------最好使用这个注解,要不然写jpql,就得写全类名
public class Customer {
    @Id //在jpa中,强制要求,每个实体类中必须有主键;否则表创建不出来
    @GeneratedValue(strategy = GenerationType.IDENTITY) //设置id是自增长的;并指定自增长的策略,一般是IDENTITY
    @Column(name="cust_id") //如果实体类中,普通属性与表的字段,有极个别的不对应,可通过@Column来指定
    private Long custId;
    @Column(name="cust_address")
    private String custAddress;
    @Column(name="cust_industry")
    private String custIndustry;
    @Column(name="cust_level")
    private String custLevel;
    @Column(name="cust_name")
    private String custName;
    @Column(name="cust_phone")
    private String custPhone;
    @Column(name="cust_source")
    private String custSource;

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

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustAddress() {
        return custAddress;
    }

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

    public String getCustIndustry() {
        return custIndustry;
    }

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

    public String getCustLevel() {
        return custLevel;
    }

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

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustPhone() {
        return custPhone;
    }

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

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }
}
           

2、配置Dao

package org.javaboy.demo1.dao;

import org.javaboy.demo1.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;


/**
 *  JpaRepository<Customer,Long>  作用:封装了基本的crud操作
 *        第一个参数:CustomerDao要操作的实体类,是谁
 *        第二个参数:实体类的id,的数据类型
 *  JpaSpecificationExecutor<Customer>  参数1:指定CustomerDao要操作的实体类
 *         JpaSpecificationExecutor作用: 封装了复杂查询【比如:分页查询】
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
    /**
     * 需求:根据用户名称,查询记录【使用jpql来实现】
     * jqpl: from Customer where custName=?
     * 使用jpql,通过@Query注解
     */
    /*@Query(value = "from org.javaboy.demo1.entity.Customer where custName=?")*/
    @Query(value="from Customer where custName = :userName")
    public Customer findJpql(@Param("userName") String userName);
    /**
     * 需求:根据客户名称和客户id,来查询客户信息
     *       jpql: from Customer where custName = ? and custId = ?
     */
    @Query(value = "from Customer where custName = :name and custId = :id")
    public Customer findCustNameAndId(@Param("id")Long id,@Param("name")String name);

    /**
     * 使用jpql完成-----更新操作
     *      案例 : 根据 客户id,来更新 客户的名称
     *
     *  sql  :update cst_customer set cust_name = ? where cust_id = ?
     *  jpql : update Customer set custName = ? where custId = ?
     *
     *  @Modifying :表示当前操作,是更新操作
     */
    @Query(value = "update Customer set custName = ?2 where custId = ?1 ")
    @Modifying
    @Transactional
    public void updateCustomer(long custId,String custName);   
}
           

3、进行单元测试

package org.javaboy.demo1;

import org.javaboy.demo1.dao.CustomerDao;
import org.javaboy.demo1.entity.Customer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class JpqlTest {
    @Autowired
    CustomerDao customerDao;

    /**
     * jqpl的简单使用---入门
     */
    @Test
    public void findJpqlTest(){
        Customer user = customerDao.findJpql("黑马程序员");
        System.out.println(user);
    }

    /**
     * jqpl使用---如何传递参数
     */
    @Test
    public void findCustNameAndIdTest(){
        Customer user = customerDao.findCustNameAndId(95L,"黑马程序员");
        System.out.println(user);
    }
    /**
     * 使用jqpl---更新操作
     */
    @Test
    public void updateCustomerTest(){
        customerDao.updateCustomer(95L,"黑马程序员6666");

    }
}
           

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CXpl3N4q-1663307663576)(图片/image-20210521201737960.png)]

4、Specifications动态查询【JpaSpecificationExecutor接口提供的】【有点用】

  • 多条件查询
  • 根据指定条件,进行查询,然后对查询结果,进行分页查询
  • 模糊查询
  • 根据指定查询条件,进行查询,然后对查询结果,进行排序

JpaSpecificationExecutor接口中的方法:使用这个里面的方法,来实现动态sql

//查询单个对象  Specification<T> spec 写查询条件的
Optional<T> findOne(@Nullable Specification<T> spec);  
//查询所有 Specification<T> spec 写查询条件的
List<T> findAll(@Nullable Specification<T> spec);

//查询全部,分页查询  Pageable pageable 分页参数
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
//查询全部,可指定排序   sort:排序参数
List<T> findAll(@Nullable Specification<T> spec, Sort sort);
//查询统计:根据查询条件,来统计出符合数据数量
long count(@Nullable Specification<T> spec);


注意:Specification是一个接口,用于构造查询条件
    使用,自定义Specification的实现类,然后实现:toPredicate方法
    /**
    * 参数1:查询的根对象(查询的任何属性都可以从根对象中获取)
      参数2:顶层查询对象,自定义查询方式(了解,一般不用)
      参数3:查询的构造器,封装了很多的查询条件
    */
    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
           

案例演示

1、创建Customer实体类

package org.javaboy.demo1.entity;

import javax.persistence.*;

@Entity
@Table(name = "cst_customer") //指定表的名称  默认情况下,实例的名字,就是表的名字
public class Customer {
    @Id //在jpa中,强制要求,每个实体类中必须有主键;否则表创建不出来
    @GeneratedValue(strategy = GenerationType.IDENTITY) //设置id是自增长的;并指定自增长的策略,一般是IDENTITY
    @Column(name="cust_id") //如果实体类中,普通属性与表的字段,有极个别的不对应,可通过@Column来指定
    private Long custId;
    @Column(name="cust_address")
    private String custAddress;
    @Column(name="cust_industry")
    private String custIndustry;
    @Column(name="cust_level")
    private String custLevel;
    @Column(name="cust_name")
    private String custName;
    @Column(name="cust_phone")
    private String custPhone;
    @Column(name="cust_source")
    private String custSource;

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

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustAddress() {
        return custAddress;
    }

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

    public String getCustIndustry() {
        return custIndustry;
    }

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

    public String getCustLevel() {
        return custLevel;
    }

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

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustPhone() {
        return custPhone;
    }

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

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }
}
           

2、创建Dao

package org.javaboy.demo1.dao;

import org.javaboy.demo1.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;


/**
 *  JpaRepository<Customer,Long>  作用:封装了基本的crud操作
 *        第一个参数:CustomerDao要操作的实体类,是谁
 *        第二个参数:实体类的id,的数据类型
 *  JpaSpecificationExecutor<Customer>  参数1:指定CustomerDao要操作的实体类
 *         JpaSpecificationExecutor作用: 封装了复杂查询【比如:分页查询】
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {

}
           

3、使用单元测试:来测试,动态sql

package org.javaboy.demo1;

import org.javaboy.demo1.dao.CustomerDao;
import org.javaboy.demo1.entity.Customer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.JpaSort;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import java.util.List;
import java.util.Optional;

@SpringBootTest
public class DongTaiSelect {
    @Autowired
    CustomerDao customerDao;
    /**
     * jpa的动态查询-------相当于mybatis的动态sql【查询一个---单添加查询】
     */
    @Test
    public void testSpec(){
        //匿名内部类
        /**
         * 自定义查询条件
         *      1.实现Specification接口,并实现它的toPredicate方法(提供的泛型:查询的对象类型)
         *      2.需要借助toPredicate方法中的两个参数(
         *          root:获取需要查询的对象属性
         *          CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
         *       )
         *  需求:根据客户名称查询,查询客户名称  为  传智播客  的客户
         *          查询条件
         *              1.查询方式:存在criteriaBuilder对象中
         *              2.比较的属性名称:存在root对象中
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                //1.获取比较的属性[看你要写的sql中,where条件部分]
                Path<Object> custName = root.get("custName");
                //2.构造查询条件  :    select * from cst_customer where cust_name = '传智播客'
                /**
                 * equal():等于
                 * 第一个参数:要比较的属性(path对象)
                 * 第二个参数:要比较的数据
                 */
           Predicate predicate = criteriaBuilder.equal(custName, "黑马程序员6666");//进行精准的匹配  (比较的属性,比较的属性的取值)
                return predicate; //返回查询条件
            }
        };
        Optional<Customer> one = customerDao.findOne(spec);
        if(one.isPresent()){
            System.out.println(one.get());
        }else {
            System.out.println("没有获取到数据");
        }

    }

    /**
     * jpa的动态查询-------相当于mybatis的动态sql【查询一个---多条件查询】
     * 需求:根据 客户名称(传智播客)和客户所属行业(it教育) 进行查询
     */
    @Test
    public void testSpec2(){

        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                //获取比较的属性[看你要写的sql中,where条件部分]
                Path<Object> custName = root.get("custName");//客户名
                Path<Object> custIndustry = root.get("custIndustry");//所属行业

                //构造查询
                //1.构造客户名的精准匹配查询
                Predicate p1 = criteriaBuilder.equal(custName, "黑马程序员6666");//第一个参数,path(属性),第二个参数,属性的取值
                //2..构造所属行业的精准匹配查询
                Predicate p2 = criteriaBuilder.equal(custIndustry, "it教育");
                //3.将多个查询条件组合到一起:组合(2个条件之间的关系:与)
                Predicate and = criteriaBuilder.and(p1, p2);//以与的形式拼接多个查询条件
                // criteriaBuilder.or(p1, p2);//以或的形式拼接多个查询条件
                return and;//返回查询条件
            }
        };
        Optional<Customer> one = customerDao.findOne(spec);
        if(one.isPresent()){
            System.out.println(one.get());
        }else {
            System.out.println("没有获取到数据");
        }

    }

    /**
     * jpa的动态查询-------相当于mybatis的动态sql【查询多个---多条件查询】
     * 需求:根据 客户名称(传智播客)和客户所属行业(it教育) 进行查询
     */
    @Test
    public void testSpec3(){

        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                //获取比较的属性[看你要写的sql中,where条件部分]
                Path<Object> custName = root.get("custName");//客户名
                Path<Object> custIndustry = root.get("custIndustry");//所属行业

                //构造查询
                //1.构造客户名的精准匹配查询
                Predicate p1 = criteriaBuilder.equal(custName, "黑马程序员6666");//第一个参数,path(属性),第二个参数,属性的取值
                //2..构造所属行业的精准匹配查询
                Predicate p2 = criteriaBuilder.equal(custIndustry, "it教育");
                //3.将多个查询条件组合到一起:组合(2个条件之间的关系:与)
                Predicate and = criteriaBuilder.and(p1, p2);//以与的形式拼接多个查询条件
                // criteriaBuilder.or(p1, p2);//以或的形式拼接多个查询条件
                return and;//返回查询条件
            }
        };
        List<Customer> all = customerDao.findAll(spec);

        if(all.size()!=0){
            for (Customer customer:all) {
                System.out.println(customer);
            }
        }else {
            System.out.println("没有获取到数据");
        }

    }
    /**
     * jpa的动态查询-------相当于mybatis的动态sql【查询多个---模糊、排序查询】
     * 需求:根据客户名称,进行模糊查询
     */
    @Test
    public void testSpec4(){

        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                //查询属性:客户名
                Path<Object> custName = root.get("custName");
                //查询方式:模糊匹配
                Predicate like = criteriaBuilder.like(custName.as(String.class), "黑马%");
                return like;
            }
        };
        /*List<Customer> all = customerDao.findAll(spec);

        if(all.size()!=0){
            for (Customer customer:all) {
                System.out.println(customer);
            }
        }else {
            System.out.println("没有获取到数据");
        }*/

        /**
         * 添加排序
         *  创建排序对象,Sort.by() 来实例化sort对象
                 * 参数1:排序的顺序(倒序,正序)
                 *    Sort.Direction.DESC:倒序
                 *     Sort.Direction.ASC : 升序
                 * 参数2:要排序的属性名称
         */
        Sort sort = Sort.by(Sort.Direction.DESC,"custId");//倒序排序
        List<Customer> list = customerDao.findAll(spec, sort);
        for (Customer customer : list) {
            System.out.println(customer);
        }

    }
    /**
     * jpa的动态查询-------相当于mybatis的动态sql【查询多个---根据指定条件,进行分页查询】
     * findAll分页查询:
     *      Specification: 查询条件
     *      Pageable:分页参数
     *           分页参数:查询的页码,每页查询的条数
     *           findAll(Specification,Pageable):带有条件的分页
     *           findAll(Pageable):没有条件的分页
     * 返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,总条数)
     */
    @Test
    public void testSpec5(){

        Specification<Customer> spec =null;
        //PageRequest对象是Pageable接口的实现类
        /**
         * 创建PageRequest的过程中,需要调用of方法
         *      参数1:当前查询的页数(从0开始,0表示第一页)
         *      参数2:每页查询的数量
         */
        Pageable pageable = PageRequest.of(1,2);
        //分页查询
        Page<Customer> page = customerDao.findAll(spec,pageable);//根据spec查询条件,查询到符合的数据记录,然后根据pageable,进行分页查询
        System.out.println(page.getContent()); //获取到分页数据
        System.out.println(page.getTotalElements());//得到数据库的总条数
        System.out.println(page.getTotalPages());//得到总页数

    }

}
           

5、复杂sql—EntityManager的使用【整个文档,看他就行了】

案例演示【单表操作,如果用sql,hql 多表也行】

数据库表的准备

/*创建客户表*/
CREATE TABLE cst_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=94 DEFAULT CHARSET=utf8;
           

1、项目的创建,直接看入门案例,即可

2、创建实体类

Customer

package org.javaboy.demo1.entity;

import javax.persistence.*;


@Entity
@Table(name = "cst_customer") //指定表的名称  默认情况下,实例的名字,就是表的名字
public class Customer {
    @Id //在jpa中,强制要求,每个实体类中必须有主键;否则表创建不出来
    @GeneratedValue(strategy = GenerationType.IDENTITY) //设置id是自增长的;并指定自增长的策略,一般是IDENTITY
    @Column(name="cust_id") //如果实体类中,普通属性与表的字段,有极个别的不对应,可通过@Column来指定
    private Long custId;
    @Column(name="cust_address")
    private String custAddress;
    @Column(name="cust_industry")
    private String custIndustry;
    @Column(name="cust_level")
    private String custLevel;
    @Column(name="cust_name")
    private String custName;
    @Column(name="cust_phone")
    private String custPhone;
    @Column(name="cust_source")
    private String custSource;

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

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustAddress() {
        return custAddress;
    }

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

    public String getCustIndustry() {
        return custIndustry;
    }

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

    public String getCustLevel() {
        return custLevel;
    }

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

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustPhone() {
        return custPhone;
    }

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

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }
}
           

3、创建对应的Dao

package org.javaboy.demo1.dao;

import org.javaboy.demo1.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 *  JpaRepository<Customer,Long>  作用:封装了基本的crud操作
 *        第一个参数:CustomerDao要操作的实体类,是谁
 *        第二个参数:实体类的id,的数据类型
 *  JpaSpecificationExecutor<Customer>  参数1:指定CustomerDao要操作的实体类
 *         JpaSpecificationExecutor作用: 封装了复杂查询【比如:分页查询】
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
    
}

           

4、测试-------crud

package org.javaboy.demo1;


import javax.persistence.*;

import org.javaboy.demo1.entity.Customer;
import org.javaboy.demo1.utils.JpaUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.List;

@SpringBootTest
public class jpaTest {


    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 添加操作
     */
    @Test
    @Transactional  //测试类中,对于事务提交方式,默认是回滚
    @Rollback(false) //取消自动回滚
    public void insertTest() {
        Customer customer=new Customer();
        customer.setCustName("aa");
        customer.setCustAddress("bbbb");
        entityManager.persist(customer);
    }
    /**
     * 更新操作
     */
    @Test
    @Transactional  //测试类中,对于事务提交方式,默认是回滚
    @Rollback(false) //取消自动回滚
    public void updateTest(){
        Customer customer=new Customer();
        customer.setCustName("aaa");
        customer.setCustAddress("bbbb");
        customer.setCustId(99L);
        entityManager.merge(customer); //更新操作
    }

    /**
     * 查询操作:根据id,进行查询
     */
    @Test
    @Transactional  //测试类中,对于事务提交方式,默认是回滚
    @Rollback(false) //取消自动回滚
    public void selectTest(){
        Customer customer = entityManager.find(Customer.class, 99L);//查询操作
        System.out.println(customer);
    }

    /**
     * 删除操作:根据id,进行查询删除对象,然后删除
     */
    @Test
    @Transactional  //测试类中,对于事务提交方式,默认是回滚
    @Rollback(false) //取消自动回滚
    public void deleteTest(){
        Customer customer = entityManager.find(Customer.class, 99L);//查询操作
        entityManager.remove(customer);//id 为99 的记录,就删除了
    }

    /**
     * 查询操作:根据指定条件,进行查询--------使用hql[jpql]查询
     */
    @Test
    @Transactional  //测试类中,对于事务提交方式,默认是回滚
    @Rollback(false) //取消自动回滚
    public void selectTest1(){
        //通过hql【也叫jpql】,来查询
        /**
         * setParameter("username","12");
         * 参数1:我写的自定义标识符
         * 参数2:给标识符,赋值
         */
        List<Customer> username = entityManager.createQuery("from Customer where custName=:username").
                                setParameter("username", "黑马程序员677666").getResultList();
        for (Customer customer:username
             ) {
            System.out.println(customer);
        }
    }
    /**
     * 查询操作:根据指定条件,进行查询--------使用sql查询【常用】
     */
    @Test
    @Transactional  //测试类中,对于事务提交方式,默认是回滚
    @Rollback(false) //取消自动回滚
    public void selectTest2(){
        //通过sql,来查询
        /**
         * createNativeQuery(参数1,参数2)
         * 参数1:写要执行的sql语句
         * 参数2:指定,sql语句,查询出的数据,和哪个类做映射,将数据封装到指定类中
         * setParameter("username","12");
         * 参数1:我写的自定义标识符
         * 参数2:给标识符,赋值
         */
        List<Customer> username = entityManager.createNativeQuery("select * from cst_customer where cust_name=:username",Customer.class).
                setParameter("username", "黑马程序员6666").getResultList();
        for (Customer customer:username
        ) {
            System.out.println(customer);
        }
    }
}