- Spring data jpa基本使用
- 一、核心方法
- 二、详细查询语法
- 三、内置方法
- 1、Sort排序
- 2、PageRequest分页
- 3、Example实例查询
- 四、Spring data jpa 注解
- 1、Repository注解
- 2、Entity注解
- 五、继承JpaSpecificationExecutor接口进行复杂查询
- 1、 JpaSpecificationExecutor提供了以下接口
- 2、首先继承JpaSpecificationExecutor
- 3、实体类task
- 4、然后serviceImpl层
- 六、Spring data jpa + QueryDSL 进行复杂查询
- 1、实体类
- 2、单表动态分页查询
- 3、多表动态查询
- 4、多表动态分页查询
- 七、改造
- 八、参考资料
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAllById(Iterable<ID> var1);
<S extends T> List<S> saveAll(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
二、详细查询语法
关键词 | 事例 | 对应sql |
---|---|---|
And | findByNameAndSex(String name,String sex) | … where x.name = ?1 and x.sex = ?2 |
Or | findByNameOrSex(String name,String sex) | … where x.name = ?1 or x.sex = ?2 |
Is,Equals | findBySex(String sex), findBySexIs(String sex), findBySexEquals(String sex) | … where x.sex = ?1 |
Between | findByStartDateBetween(String time1,String time2) | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan(Integer age) | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual(Integer age) | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan(Integer age) | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual(Integer age) | … where x.age >= ?1 |
After | findByStartDateAfter(String time) | … where x.startDate > ?1 |
Before | findByStartDateBefore(String time) | … where x.startDate < ?1 |
IsNull | findByAgeIsNull() | … where x.age is null |
IsNotNull, NotNull | findByAgeIsNotNull(), findByAgeNotNull() | … where x.age is not null … where x.age not null |
Like | findBySexLike(String sex) | … where x.sex like ?1 |
NotLike | findBySexNotLike(String sex) | … where x.sex not like ?1 |
StartingWith | findBySexStartingWith(String sex) | … where x.sex like ?1 (parameter bound with appended %) |
EndingWith | findBySexEndingWith(String sex) | … where x.sex like ?1 (parameter bound with prepended %) |
Containing | findBySexContaining(String sex) | … where x.sex like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByNameDesc(String age) | … where x.age = ?1 order by x.name desc |
Not | findByNameNot(String name) | … where x.name <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection ages) | … where x.age not in ?1 |
TRUE | findByActiveTrue() | … where x.active = true |
FALSE | findByActiveFalse() | … where x.active = false |
IgnoreCase | findBySexIgnoreCase(String sex) | … where UPPER(x.sex) = UPPER(?1) |
First,Top 默认为1 | findFirst10ByName(String name), findTop10ByName(String name) | … where x.name = ?1 limit 0,10 |
Sort sort =new Sort(Sort.Direction.ASC,"id");
//其中第一个参数表示是降序还是升序(此处表示升序)
//第二个参数表示你要按你的 entity(记住是entity中声明的变量,不是数据库中表对应的字段)中的那个变量进行排序
2、PageRequest分页
PageRequest pageRequest = new PageRequest(index, num, sort);
//index偏移量 num查询数量 sort排序
DemoBean demoBean = new DemoBean();
demoBean.setAppId(appId); //查询条件
//创建查询参数
Example<DemoBean> example = Example.of(demoBean);
//获取排序对象
Sort sort = new Sort(Sort.Direction.DESC, "id");
//创建分页对象
PageRequest pageRequest = new PageRequest(index, num, sort);
//分页查询
return demoRepository.findAll(example, pageRequest).getContent();
3、Example实例查询
Person person = new Person();
person.setFirstname("Dave"); //Firstname = 'Dave'
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("name", GenericPropertyMatchers.startsWith()) //姓名采用“开始匹配”的方式查询
.withIgnorePaths("int"); //忽略属性:是否关注。因为是基本类型,需要忽略掉
Example<Person> example = Example.of(person, matcher); //Example根据域对象和配置创建一个新的ExampleMatcher
四、Spring data jpa 注解
1、Repository注解
@Modifying //做update *** 作时需要添加
@Query // 自定义Sql
@Modifying // 执行修改时使用
@Transactional //事务
@Async //异步 *** 作
@Query(value = "SELECT * FROM USERS WHERE X = ?1", nativeQuery = true)
User findByEmailAddress(String X);
@Query("select u from User u where u.firstname = :firstname") //不加nativeQuery应使用HQL(占位符)
User findByLastnameOrFirstname(@Param("lastname") String lastname);
@Transactional
@Modifying
@Query("update User set firstname = ?1 where id = ?2")
User findByLastnameOrFirstname(String lastname, String id);
@Transactional
@Modifying
@Query("delete from User where id = ?1")
User findByLastnameOrFirstname(String id);
2、Entity注解
@Entity //不写@Table默认为user
@Table(name="t_user") //自定义表名
public class user {
@Id //主键
@GeneratedValue(strategy = GenerationType.AUTO)//采用数据库自增方式生成主键
//JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO.
//TABLE:使用一个特定的数据库表格来保存主键。
//SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
//IDENTITY:主键由数据库自动生成(主要是自动增长型)
//AUTO:主键由程序控制。
@Transient //此字段不与数据库关联
@Version//此字段加上乐观锁
//字段为name,不允许为空,用户名唯一
@Column(name = "name", unique = true, nullable = false)
private String name;
@Temporal(TemporalType.DATE)//生成yyyy-MM-dd类型的日期
//出参时间格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
//入参时,请求报文只需要传入yyyymmddhhmmss字符串进来,则自动转换为Date类型数据
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
private Date createTime;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
五、继承JpaSpecificationExecutor接口进行复杂查询
1、 JpaSpecificationExecutor提供了以下接口
public interface JpaSpecificationExecutor {
T findOne(Specification spec);
List findAll(Specification spec);
Page findAll(Specification spec, Pageable pageable);
List findAll(Specification spec, Sort sort);
long count(Specification spec);
}
//其中Specification就是需要我们传入查询方法的参数,它是一个接口
public interface Specification {
Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb);
}
JPA 2.0 criteria api 参考:
http://blog.csdn.net/dracotianlong/article/details/28445725
http://developer.51cto.com/art/200911/162722.htm
2、首先继承JpaSpecificationExecutorpublic interface TaskDao extends JpaSpecificationExecutor<Task>{
}
3、实体类task
@Entity
@Table(name = "tb_task")
public class Task {
private Long id ;
private String taskName ;
private Date createTime ;
private Project project;
private String taskDetail ;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name = "task_name")
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
@Column(name = "create_time")
@DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss")
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Column(name = "task_detail")
public String getTaskDetail() {
return taskDetail;
}
public void setTaskDetail(String taskDetail) {
this.taskDetail = taskDetail;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "project_id")
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
4、然后serviceImpl层
通过重写toPredicate方法,返回一个查询 Predicate,spring data jpa会帮我们进行查询。
@Service
public class TaskService {
@Autowired TaskDao taskDao ;
/**
* 复杂查询测试
* @param page
* @param size
* @return
*/
public Page<Task> findBySepc(int page, int size){
PageRequest pageReq = this.buildPageRequest(page, size);
Page<Task> tasks = this.taskDao.findAll(new MySpec(), pageReq);
//传入了new MySpec() 既下面定义的匿名内部类 其中定义了查询条件
return tasks;
}
/**
* 建立分页排序请求
* @param page
* @param size
* @return
*/
private PageRequest buildPageRequest(int page, int size) {
Sort sort = new Sort(Direction.DESC,"createTime");
return new PageRequest(page,size, sort);
}
/**
* 建立查询条件
*/
private class MySpec implements Specification<Task>{
@Override
public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//1.混合条件查询
Path<String> exp1 = root.get("taskName");
Path<Date> exp2 = root.get("createTime");
Path<String> exp3 = root.get("taskDetail");
Predicate predicate = cb.and(cb.like(exp1, "%taskName%"),cb.lessThan(exp2, new Date()));
return cb.or(predicate,cb.equal(exp3, "kkk"));
/* 类似的sql语句为:
Hibernate:
select
count(task0_.id) as col_0_0_
from
tb_task task0_
where
(
task0_.task_name like ?
)
and task0_.create_time
or task0_.task_detail=?
*/
//2.多表查询
Join<Task,Project> join = root.join("project", JoinType.INNER);
Path<String> exp4 = join.get("projectName");
return cb.like(exp4, "%projectName%");
/* Hibernate:
select
count(task0_.id) as col_0_0_
from
tb_task task0_
inner join
tb_project project1_
on task0_.project_id=project1_.id
where
project1_.project_name like ?*/
return null ;
}
}
}
如果你觉得,每次都要写一个类来实现Specification很麻烦,你可以这么写
public class TaskSpec {
public static Specification<Task> method1(){
return new Specification<Task>(){
@Override
public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return null;
}
};
}
public static Specification<Task> method2(){
return new Specification<Task>(){
@Override
public Predicate toPredicate(Root<Task> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return null;
}
};
}
}
那么用的时候,我们就这么用
Page<Task> tasks = this.taskDao.findAll(TaskSpec.method1(), pageReq);
六、Spring data jpa + QueryDSL 进行复杂查询
- QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查询。
- Querydsl可以通过一组通用的查询API为用户构建出适合不同类型ORM框架或者是SQL的查询语句,也就是说QueryDSL是基于各种ORM框架以及SQL之上的一个通用的查询框架。
- 借助QueryDSL可以在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建查询。目前QueryDSL支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。
//城市类
@Entity
@Table(name = "t_city", schema = "test", catalog = "")
public class TCity {
//省略JPA注解标识
private int id;
private String name;
private String state;
private String country;
private String map;
}
//旅馆类
@Entity
@Table(name = "t_hotel", schema = "test", catalog = "")
public class THotel {
//省略JPA注解标识
private int id;
private String name;
private String address;
private Integer city;//保存着城市的id主键
}
2、单表动态分页查询
Spring Data JPA中提供了QueryDslPredicateExecutor接口,用于支持QueryDSL的查询 *** 作
public interface tCityRepository extends JpaRepository<TCity, Integer>, QueryDslPredicateExecutor<TCity> {
}
这样的话单表动态查询就可以参考如下代码:
//查找出Id小于3,并且名称带有`shanghai`的记录.
//动态条件
QTCity qtCity = QTCity.tCity; //SDL实体类
//该Predicate为querydsl下的类,支持嵌套组装复杂查询条件
Predicate predicate = qtCity.id.longValue().lt(3).and(qtCity.name.like("shanghai"));
//分页排序
Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC,"id"));
PageRequest pageRequest = new PageRequest(0,10,sort);
//查找结果
Page<TCity> tCityPage = tCityRepository.findAll(predicate,pageRequest);
3、多表动态查询
/**
* 关联查询示例,查询出城市和对应的旅店
* @param predicate 查询条件
* @return 查询实体
*/
@Override
public List<Tuple> findCityAndHotel(Predicate predicate) {
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity,QTHotel.tHotel)
.from(QTCity.tCity)
.leftJoin(QTHotel.tHotel)
.on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue()));
//添加查询条件
jpaQuery.where(predicate);
//拿到结果
return jpaQuery.fetch();
}
城市表左连接旅店表,当该旅店属于这个城市时查询出两者的详细字段,存放到一个Tuple的多元组中.相比原生sql,简单清晰了很多.
那么该怎么调用这个方法呢?
@Test
public void findByLeftJoin(){
QTCity qtCity = QTCity.tCity;
QTHotel qtHotel = QTHotel.tHotel;
//查询条件
Predicate predicate = qtCity.name.like("shanghai");
//调用
List<Tuple> result = tCityRepository.findCityAndHotel(predicate);
//对多元组取出数据,这个和select时的数据相匹配
for (Tuple row : result) {
System.out.println("qtCity:"+row.get(qtCity));
System.out.println("qtHotel:"+row.get(qtHotel));
System.out.println("--------------------");
}
System.out.println(result);
}
这样做的话避免了返回Object[]数组,下面是自动生成的sql语句:
select
tcity0_.id as id1_0_0_,
thotel1_.id as id1_1_1_,
tcity0_.country as country2_0_0_,
tcity0_.map as map3_0_0_,
tcity0_.name as name4_0_0_,
tcity0_.state as state5_0_0_,
thotel1_.address as address2_1_1_,
thotel1_.city as city3_1_1_,
thotel1_.name as name4_1_1_
from
t_city tcity0_
left outer join
t_hotel thotel1_
on (
cast(thotel1_.city as signed)=cast(tcity0_.id as signed)
)
where
tcity0_.name like ? escape '!'
4、多表动态分页查询
分页查询对于queryDSL无论什么样的sql只需要写一遍,会自动转换为相应的count查询
@Override
public QueryResults<Tuple> findCityAndHotelPage(Predicate predicate,Pageable pageable) {
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity.id,QTHotel.tHotel)
.from(QTCity.tCity)
.leftJoin(QTHotel.tHotel)
.on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue()))
.where(predicate)
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
//拿到分页结果
return jpaQuery.fetchResults();
}
和上面不同之处在于这里使用了offset
和limit
限制查询结果.并且返回一个QueryResults,该类会自动实现count查询和结果查询,并进行封装.
调用形式如下:
@Test
public void findByLeftJoinPage(){
QTCity qtCity = QTCity.tCity;
QTHotel qtHotel = QTHotel.tHotel;
//条件
Predicate predicate = qtCity.name.like("shanghai");
//分页
PageRequest pageRequest = new PageRequest(0,10);
//调用查询
QueryResults<Tuple> result = tCityRepository.findCityAndHotelPage(predicate,pageRequest);
//结果取出
for (Tuple row : result.getResults()) {
System.out.println("qtCity:"+row.get(qtCity));
System.out.println("qtHotel:"+row.get(qtHotel));
System.out.println("--------------------");
}
//取出count查询总数
System.out.println(result.getTotal());
}
生成的原生count查询sql,当该count查询结果为0的话,则直接返回,并不会再进行具体数据查询:
select
count(tcity0_.id) as col_0_0_
from
t_city tcity0_
left outer join
t_hotel thotel1_
on (
cast(thotel1_.city as signed)=cast(tcity0_.id as signed)
)
where
tcity0_.name like ? escape '!'
select
tcity0_.id as id1_0_0_,
thotel1_.id as id1_1_1_,
tcity0_.country as country2_0_0_,
tcity0_.map as map3_0_0_,
tcity0_.name as name4_0_0_,
tcity0_.state as state5_0_0_,
thotel1_.address as address2_1_1_,
thotel1_.city as city3_1_1_,
thotel1_.name as name4_1_1_
from
t_city tcity0_
left outer join
t_hotel thotel1_
on (
cast(thotel1_.city as signed)=cast(tcity0_.id as signed)
)
where
tcity0_.name like ? escape '!' limit ?
查看打印,可以发现对应的city也都是同一个对象,hotel是不同的对象.
JPAQueryFactory factory = new JPAQueryFactory(entityManager);
factory.select($.pcardCardOrder)
.select($.pcardVcardMake.vcardMakeDes)
.select($.pcardVtype.cardnumRuleId,$.pcardVtype.vtypeNm)
.select($.pcardCardbin)
.leftJoin($.pcardVcardMake).on($.pcardCardOrder.makeId.eq($.pcardVcardMake.vcardMakeId))
//......省略
查询条件使用Predicate
代替,放在service拼接,或者写一个生产条件的工厂都可以.
jpaQuery.where(predicate);
最后的分页处理就和之前的一样了
jpaQuery.offset(pageable.getOffset())
.limit(pageable.getPageSize());
return jpaQuery.fetchResults();
八、参考资料
- 使用QueryDSL
- Spring Data JPA 实例查询
- Spring Data JPA - Reference Documentation
- Querydsl Reference Guide
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)