一、JPA的关键字拼凑法 - 单表SQL操作
Keywrod | Sample | JPQL snippet |
---|---|---|
Distinct | findDistinctByLastnameAndFirstname | select distinct … where x.lastname = ?1 and x.firstname = ?2 |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is, Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull, Null | findByAge(Is)Null | … where x.age is null |
IsNotNull, NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
二、单表SQL操作-关键字拼凑法的例子
1. 创建SpringBoot项目并配置相关的内容
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- application.properties
# MySql配置
spring.datasource.url=jdbc:mysql://localhost:3306/cloud_study?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=####
spring.datasource.password=####
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 支持SQL输出
spring.jpa.show-sql=true
# 格式化 SQL 输出
spring.jpa.properties.hibernate.format_sql=true
# 开启数据库表结构自动更新,让表根据Entity类的变化而变化,有多个选项 如update, create、create-drop, 生产环境下非必要不要开启
spring.jpa.hibernate.ddl-auto=create #这里表示每次运行程序都删除掉表,然后重新构建
2. 创建实体类Person
package cn.lazyfennec.springdatajpademo2.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @Author: Neco
* @Description:
* @Date: create in 2022/4/30 17:41
*/
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Person {
@Id
// 配置生成策略为UUID
@GenericGenerator(name = "uuid", strategy = "uuid")
@GeneratedValue(generator = "uuid")
private String id;
@Column
private String name;
@Column
private String sex;
@Column
private Integer age;
@Column
private Boolean getmarried; // 避免使用拼凑法时语义错误,故不用驼峰而用小写
}
3. 创建PersonRepository
package cn.lazyfennec.springdatajpademo2.repository;
import cn.lazyfennec.springdatajpademo2.entity.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
* @Author: Neco
* @Description:
* @Date: create in 2022/4/30 17:47
*/
public interface PersonRepository extends JpaRepository<Person, String> {
// 1. 查询年龄小于等于22岁的人
List<Person> findAllByAgeLessThanEqual(Integer age);
// 2. 查询出年龄在20岁到22岁之间并且性别是男的人
List<Person> findAllByAgeBetweenAndSexEquals(Integer age1, Integer age2, String sex);
// 3. 查询出已经结婚并且性别是男的人
List<Person> findAllByGetmarriedIsTrueAndSexEquals(String sex);
}
4. 测试
package cn.lazyfennec.springdatajpademo2;
import cn.lazyfennec.springdatajpademo2.entity.Person;
import cn.lazyfennec.springdatajpademo2.repository.PersonRepository;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@SpringBootTest
class SpringDataJpaDemo2ApplicationTests {
@Resource
private PersonRepository personRepository;
@Test
void contextLoads() {
initPeople();
// 1. 查询年龄小于等于22岁的人
System.out.println(personRepository.findAllByAgeLessThanEqual(22));
// 2. 查询出年龄在20岁到22岁之间并且性别是男的人
System.out.println(personRepository.findAllByAgeBetweenAndSexEquals(20, 22, "男"));
// 3. 查询出已经结婚并且性别是男的人
System.out.println(personRepository.findAllByGetmarriedIsTrueAndSexEquals("男"));
}
// 初始化数据
private void initPeople() {
List<Person> list = new ArrayList<>();
Collections.addAll(list,
Person.builder().name("张三").sex("男").age(18).getmarried(false).build(),
Person.builder().name("李四").sex("男").age(25).getmarried(true).build(),
Person.builder().name("王五").sex("女").age(18).getmarried(false).build(),
Person.builder().name("马六").sex("男").age(22).getmarried(false).build(),
Person.builder().name("赵七").sex("女").age(23).getmarried(true).build()
);
personRepository.saveAll(list);
}
}
结果如下
Hibernate:
select
person0_.id as id1_0_,
person0_.age as age2_0_,
person0_.getmarried as getmarri3_0_,
person0_.name as name4_0_,
person0_.sex as sex5_0_
from
person person0_
where
person0_.age<=?
[Person(id=4028b8818079f1cb018079f1d45b0000, name=张三, sex=男, age=18, getmarried=false), Person(id=4028b8818079f1cb018079f1d46f0002, name=王五, sex=女, age=18, getmarried=false), Person(id=4028b8818079f1cb018079f1d46f0003, name=马六, sex=男, age=22, getmarried=false)]
Hibernate:
select
person0_.id as id1_0_,
person0_.age as age2_0_,
person0_.getmarried as getmarri3_0_,
person0_.name as name4_0_,
person0_.sex as sex5_0_
from
person person0_
where
(
person0_.age between ? and ?
)
and person0_.sex=?
[Person(id=4028b8818079f1cb018079f1d46f0003, name=马六, sex=男, age=22, getmarried=false)]
Hibernate:
select
person0_.id as id1_0_,
person0_.age as age2_0_,
person0_.getmarried as getmarri3_0_,
person0_.name as name4_0_,
person0_.sex as sex5_0_
from
person person0_
where
person0_.getmarried=1
and person0_.sex=?
[Person(id=4028b8818079f1cb018079f1d46f0001, name=李四, sex=男, age=25, getmarried=true)]
三、关键字拼凑无法解决的问题
- 实体类的属性名与表的字段无法映射,导致关键字找不到
- CRUD操作方式比较另类或者是不想用关键字的方法
- 涉及到了多表操作
四、使用注解来手写SQL语句
两种方式
- 使用SQL 语句来书写SQL
- 使用HQL语句来书写SQL
注意事项
- 如果是删改操作,需要加@Modifying和@Transactional注解
- 如果是SQL语句,请在@Query注解上加上NativeQuery=true的属性
- 传参的方式有两种:
- 使用?+数字的方式,数字从1开始,代表第几个参数
- 使用:+参数名的方式,这种方式最好配合@Param注解一起使用
例子
- 根据name来模糊删除一个person数据
// 4. 根据name来模糊删除一个person数据
@Modifying
@Transactional
@Query(value = "delete from Person where name like %:name%")
void deleteByPersonName(@Param("name") String name);
- 使用HQL或sql来书写一个查询语句,查询年龄在20-22岁,性别为女的人
// 5. 使用HQL或sql来书写一个查询语句,查询年龄在20-22岁,性别为女的人
//@Query(value = "select * from person where age between 20 and 22 and sex='女'", nativeQuery = true) // 表示使用SQL语句
@Query(value = "select p from Person p where p.age between 20 and 22 and p.sex='女'") // 使用HQL语句
List<Person> findPeople1();
- 使用SPEL表达式来完成person表的修改操作
// 6. 使用SPEL表达式来完成person表的修改操作
@Modifying
@Transactional
//@Query(value = "update person set name=:#{#person.name},sex=:#{#person.sex}, age=:#{#person.age} " +
// " where id=:#{#person.id}", nativeQuery = true) // SQL
@Query(value = "update Person p set p.name=:#{#person.name},p.sex=:#{#person.sex}, p.age=:#{#person.age} " +
" where p.id=:#{#person.id}") // HQL
void updatePerson(@Param("person") Person person);
五、Spring Data JPA 多表查询
image.png1. 例子
- 导入数据库表
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`BID` int(4) NOT NULL AUTO_INCREMENT,
`BNAME` varchar(255) DEFAULT NULL,
`BPRICE` double(4,2) DEFAULT NULL,
`PID` varchar(255) DEFAULT NULL,
PRIMARY KEY (`BID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
#
# Data for table "book"
#
INSERT INTO `book` VALUES
(1,'三国演义',19.90,'402882f870d847990170d847a73c0000'),
(2,'红楼梦',29.90,'402882f870d847990170d847a73c0000'),
(3,'西游记',39.90,'402882f870d847990170d847a7590003'),
(4,'水浒传',28.80,'402882f870d847990170d847a73c0000'),
(5,'变形金刚',30.00,'402882f870d847990170d847a7590003'),
(6,'樱桃小丸子',48.80,'402882f870d847990170d847a7590003'),
(7,'大宅门',18.00,'402882f870d847990170d847a7590003'),
(8,'世界史',58.80,'402882f870d847990170d847a73c0000'),
(9,'人类史',60.00,'402882f870d847990170d847a73c0000'),
(10,'生命简史',55.80,'402882f870d847990170d847a73c0000');
- 逆向生成Book类
package cn.lazyfennec.springdatajpademo2.entity;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Objects;
/**
* @Author: Neco
* @Description:
* @Date: create in 2022/4/30 20:54
*/
@Entity
public class Book {
private int bid;
private String bname;
private Double bprice;
private String pid;
@Id
@Column(name = "BID")
public int getBid() {
return bid;
}
public void setBid(int bid) {
this.bid = bid;
}
@Basic
@Column(name = "BNAME")
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
@Basic
@Column(name = "BPRICE")
public Double getBprice() {
return bprice;
}
public void setBprice(Double bprice) {
this.bprice = bprice;
}
@Basic
@Column(name = "PID")
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return bid == book.bid &&
Objects.equals(bname, book.bname) &&
Objects.equals(bprice, book.bprice) &&
Objects.equals(pid, book.pid);
}
@Override
public int hashCode() {
return Objects.hash(bid, bname, bprice, pid);
}
}
- 联表查询
- 联表查询-根据书名来查该书籍的拥有者
//7、联表查询-根据书名来查该书籍的拥有者
@Query(value = "select p from Person p inner join Book b on p.id=b.pid where b.bname=:bname")
Person findPersonByBname(@Param("bname") String bname);
- 使用数据接口接收查询数据
- 创建数据接口 PersonInfo
package cn.lazyfennec.springdatajpademo2.entity; public interface PersonInfo { String getPid(); String getPname(); String getPsex(); String getPage(); String getGetmerried(); Integer getBid(); String getBname(); double getBprice(); }
- 联表查询-根据用户id来查询person和book,并用PersonInfo接口来接收,这里必须使用别名
//8、联表查询-根据用户id来查询person和book @Query(value = "select p.id as id,p.name as name,p.sex as sex,p.getmarried as getmarried," + "b.bid as bid,b.bname as bname,b.bprice as bprice from Person p inner join Book b on p.id=b.pid " + "where p.id=:id") List<PersonInfo> findAllInfo(@Param("id") String id);
- 直接使用List/Map等集合嵌套的方式来获取到接收数据
- List
@Query(value = "select p.pid as pid,p.pname as pname,p.psex as psex,p.getmarried as getmarried," + "b.bid as bid,b.bname as bname,b.bprice as bprice from Person p inner join Book b on p.pid=b.pid " + "where p.pid=:pid") List<Object> findAllInfo1(@Param("pid") String pid);
- Map
@Query(value = "select p.id as id,p.name as name,p.sex as sex,p.getmarried as getmarried," + "b.bid as bid,b.bname as bname,b.bprice as bprice from Person p inner join Book b on p.id=b.pid " + "where p.id=:id") List<Map<String,Object>> findAllInfo2(@Param("id") String id);
六、多表复杂查询的又一种方式-QueryDSL
- 什么是QueryDSL
QueryDSL仅仅是一个通用的查询框架,专注于通过JavaAPI构建类型安全的Sql查询,也可以说QueryDSL是基于各种ORM框架以及Sql之上的一个通用的查询框架,QueryDSL的查询,类是于SQL查询,很全面只不过一个是用SQL—个是用代码来代替SQL。 - 建议
单表乃至简单的多表操作,都不推荐使用QueryDSL,使用JPA自带API简洁又效率,但是涉及太复杂的查询,推荐使用QueryDSL -
例子
例子 - 官网介绍
http://querydsl.com/static/querydsl/latest/reference/html/
更多知识,请点击关注查看我的主页信息哦~
网友评论