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);
}
}
}