结论如下:
用querydsl的方式。
既然提供了这种方式就用吧。单实体,单表多条记录,动态条件直接写,不用写dao层。
多表的,大部分用querydsl都能实现。不能实现的,用sql实现。
返回值方面 还是和以前一样,返回主实体类,附加的挂在对应的类属性上。需要很高效率的用dto返回。
代码如下:
package com.xxx.xxxxx.service.impl;
import com.querydsl.core.BooleanBuilder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.Specification;
import org.springframework.stereotype.Service;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
@Service
public class DemoStudentServiceImpl extends BaseServiceImpl
implements DemoStudentService {
@Autowired
private DemoStudentRepository demoStudentRepository;
@Autowired
public DemoStudentServiceImpl(DemoStudentRepository demoStudentRepository) {
super(demoStudentRepository);
}
@Override
public int updateNameByStudentGuid(String name, String studentGuid){
return demoStudentRepository.updateNameByStudentGuid(name,studentGuid);
}
@Override
public long countStudentBySex(String sex) {
return demoStudentRepository.countBySex(sex);
}
@Override
public int deleteStudentByName(String name) {
//return demoStudentRepository.deleteByNameQuick(name);
return demoStudentRepository.deleteXXXByName(name);//deleteXXXByXXX ,delte 后面跟着的,by前面跟着的没所谓起啥。都支持。
}
@Override
public List< DemoStudent > removeStudentByName(String name) {
return demoStudentRepository.removeStudentByName(name);
}
/**
* Query 注解加 Modifying 注解 方式更新多条记录
* @param name
* @param birthday
* @return
*/
public int updateNameByAge(String name, String birthday) {
return demoStudentRepository.updateNameByAge(name,birthday);
}
/**
* 查询单个
* @param name
* @return
*/
@Override
public DemoStudent findByNameAndAge(String name,String age) {
return demoStudentRepository.findByNameAndAge(name,age);
}
/**
* 查询单个(动态条件)
* @param name
* @param age
* @return
*/
@Override
public DemoStudent findStudentPredicate(String name, String age) {
QDemoStudent qDemoStudent=QDemoStudent.demoStudent;
BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.isNotBlank(name)){
builder.and(qDemoStudent.name.like("%"+name+"%"));
}
if(StringUtils.isNotBlank(age)){
builder.and(qDemoStudent.age.eq(age));
}
//多条记录会报错
return demoStudentRepository.findOne(builder).get();
}
/**
* 单表集合 动态查询 predicate 方式
* @param page
* @param rows
* @param name
* @param age
* @return
*/
@Override
public Page
//查找所有的的分页
//demoStudentRepository.findAll(PageRequest.of(page-1,rows));
//分页加排序加参数查询
QDemoStudent qDemoStudent=QDemoStudent.demoStudent;
BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.isNotBlank(name)){
builder.and(qDemoStudent.name.like("%"+name+"%"));
}
if(StringUtils.isNotBlank(age)){
builder.or(qDemoStudent.age.eq(age));
}
return demoStudentRepository.findAll(builder,PageRequest.of(page-1,rows,Sort.by("age").descending()));
}
/**
* 多表集合 动态查询 predicate 方式,返回方式为主类,副类或者副类的其他字段以其他方式set进主类
* @param page
* @param rows
* @param name
* @param age
* @return
*/
@Override
public Page
//分页加排序加参数查询
QDemoStudent qDemoStudent=QDemoStudent.demoStudent;
BooleanBuilder builder = new BooleanBuilder();
if(StringUtils.isNotBlank(name)){
builder.and(qDemoStudent.name.like("%"+name+"%"));
}
if(StringUtils.isNotBlank(age)){
builder.or(qDemoStudent.age.eq(age));
}
return demoStudentRepository.pageStudentClassListPredicate(builder,PageRequest.of(page-1,rows));
}
/**
* 多表查询,采用dsl的方式,返回自定义对象
* @return
*/
@Override
public List < DemoStudentVO > findStudentClassDslDTO(){
return demoStudentRepository.findStudentClassDslDTO();
}
/**
* 多表查询,原始查询的方式(native sql),并且按指定的resultSetMapping返回
* @return
*/
@Override
public Page
return demoStudentRepository.pageStudentClassSqlMapResult(PageRequest.of(page-1,rows),name,age);
}
/**
* 多表集合 多条件,query注解, new DemoStudentVO 对象返回, 需要实体manytoone支持表关联
* @param page
* @param rows
* @param name
* @param age
* @return
*/
@Override
public Page
return null;
//return demoStudentRepository.pageStudentClassListJPQLDTO(name,age,PageRequest.of(page-1,rows,Sort.by("age").descending()));
}
/**
* 多表集合 多条件,query注解,native sql 方式,默认返回object[]
* @param page
* @param rows
* @param name
* @param age
* @return
*/
@Override
public Page < Object[] > pageStudentClassListSql(Integer page, Integer rows, String name, String age) {
return demoStudentRepository.pageStudentClassListSql(name,age,PageRequest.of(page-1,rows,Sort.by("age").descending()));
}
/**
* 使用Specification 方式分页
* @param page
* @param rows
* @param name
* @param age
* @return
*/
public Page < DemoStudent > pageStudentClassSpecification(Integer page, Integer rows, String name, String age) {
//规格定义
Specification
/**
* 构造断言
* @param root 实体对象引用
* @param query 规则查询对象
* @param cb 规则构建对象
* @return 断言
*/
@Override
public Predicate toPredicate(Root
List
if(StringUtils.isNotBlank(name)){ //添加断言
Predicate likeName = cb.like(root.get("name").as(String.class),name+"%");
predicates.add(likeName);
}
return cb.and(predicates.toArray(new Predicate[0]));
}
};
//分页信息
Pageable pageable = PageRequest.of(page-1,rows); //页码:前端从1开始,jpa从0开始,做个转换
//查询
return demoStudentRepository.findAll(specification,pageable);
}
}
package com.xxxx.xxxxxx.repository.custom.impl;
import com.querydsl.core.QueryResults;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import freemarker.template.utility.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.SQLQuery;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.Querydsl;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DemoStudentRepositoryImpl implements DemoStudentRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
//查询工厂实体
private JPAQueryFactory queryFactory;
@PostConstruct
public void init(){
queryFactory = new JPAQueryFactory(entityManager);
}
/**
* 多表查询,采用dsl的方式,返回单个实体的page列表,单实体中有其他实体
* @return
*/
@Override
public Page pageStudentClassListPredicate(Predicate predicate, Pageable pageable){
QDemoStudent qDemoStudent = QDemoStudent.demoStudent;
QDemoClass qDemoClass = QDemoClass.demoClass;
JPAQuery jpaQuery = queryFactory.select(qDemoStudent,qDemoClass)
.from(qDemoStudent).leftJoin(qDemoClass).on(qDemoStudent.classGuid.eq(qDemoClass.classGuid))
.where(predicate)
.orderBy(qDemoStudent.age.desc())
.offset(pageable.getOffset())
.limit(pageable.getPageSize());
QueryResults result=jpaQuery.fetchResults();
List lsStudent=new ArrayList ();
for (Tuple row : result.getResults()) {
DemoStudent student= row.get(qDemoStudent);
student.setDemoClass(row.get(qDemoClass));
lsStudent.add(student);
}
Page pageDemoStudent=new PageImpl < DemoStudent >(lsStudent,pageable,result.getTotal());
return pageDemoStudent;
}
/**
* 多表查询,采用dsl的方式,返回自定义对象
* @return
*/
@Override
public List < DemoStudentVO > findStudentClassDslDTO(){
QDemoStudent qDemoStudent = QDemoStudent.demoStudent;
QDemoClass qDemoClass = QDemoClass.demoClass;
//用下面的办法或者直接返回Tuple 对象,一个个set,例如:dto.setPrice(tuple.get(_Q_good.price));
return queryFactory
.select(
Projections.bean(
DemoStudentVO.class,//返回自定义实体的类型 这种方式,dto对象不要用构造函数,会报错
qDemoStudent.studentGuid,
qDemoStudent.name,
qDemoStudent.age,
qDemoStudent.birthday,
qDemoClass.className,
qDemoClass.remark.as("classRemark")//使用别名对应dto内的typeId
)
)
.from(qDemoStudent,qDemoClass)//构建两表笛卡尔集
.where(qDemoStudent.classGuid.eq(qDemoClass.classGuid))//关联两表
.orderBy(qDemoStudent.age.desc())//倒序
.fetch();
}
/**
* 多表查询,原始查询的方式(native sql),并且按以前hibernate方式返回
* @return
*/
@Override
public Page pageStudentClassSqlMapResult(Pageable pageable, String name, String age) {
StringBuilder countSelectSql = new StringBuilder();
countSelectSql.append("select count(1) from demo_student s "+
" left join demo_class c on s.class_guid=c.class_guid where 1=1 ");
StringBuilder selectSql = new StringBuilder();
selectSql.append("select {s.*},{c.*} from demo_student s "+
" left join demo_class c on s.class_guid=c.class_guid where 1=1 ");
Map params = new HashMap<>();
StringBuilder whereSql = new StringBuilder();
if(StringUtils.isNotBlank(name)){
whereSql.append(" and name=:name ");
params.put("name",name);
}
if(StringUtils.isNotBlank(age)){
whereSql.append(" and age=:age ");
params.put("age",age);
}
String countSql = new StringBuilder().append(countSelectSql).append(whereSql).toString();
Query countQuery = entityManager.createNativeQuery(countSql);
for(Map.Entry entry:params.entrySet()){
countQuery.setParameter(entry.getKey(),entry.getValue());
}
BigInteger totalCount = (BigInteger)countQuery.getSingleResult();
String querySql = new StringBuilder().append(selectSql).append(whereSql).toString();
// select s.*,c.* 这种,两个表有相同字段的,因为第二个表的对应字段会用第一个表的对应字段,数据信息不对。
//Query query = this.entityManager.createNativeQuery(querySql,"StudentResults");
Query query = this.entityManager.createNativeQuery(querySql);
for(Map.Entry entry:params.entrySet()){
query.setParameter(entry.getKey(),entry.getValue());
}
query.setFirstResult((int)pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
//query.unwrap(SQLQuery.class).addEntity("s",DemoStudent.class).addEntity("c",DemoClass.class);
query.unwrap(NativeQuery.class).addEntity("s",DemoStudent.class).addEntity("c",DemoClass.class);
List result =query.getResultList();//是object[]数组,第一个元素是demo_student对象,第二个元素是demo_class对象
List lsStudent=new ArrayList ();
for (Object row : result) {
DemoStudent demoStudent=(DemoStudent)((Object[])row)[0];
DemoClass demoClass=(DemoClass)((Object[])row)[1];
demoStudent.setDemoClass(demoClass);
lsStudent.add(demoStudent);
}
Page pageDemoStudent=new PageImpl < DemoStudent >(lsStudent,pageable,totalCount.longValue());
return pageDemoStudent;
}
}
参考:http://www.ityouknow.com/spring-boot.html
https://www.cnblogs.com/ityouknow/p/5891443.html
几种方式
1.hibernate方式 字段上面加manytoone什么之类的。不喜欢这种,有时候对象之间关联很多,不知道什么时候调用了。虽然有懒加载,但是有时候关联的另外的表很大,关联的表很多,就不可控,多表还是sql语句吧。
2.jpql方式1 通过new 对象的方式传入参数,作为返回对象,自定义对象
参考:https://blog.csdn.net/phapha1996/article/details/78994395
@Query(value = "select new org.fage.vo.UserOutputVO(u.name, u.email, d.name as departmentName, count(r.id) as roleNum) from User u "
+ "left join u.department d left join u.roles r group by u.id")
Page
这里注意一下,VO中的构造方法参数一定要与查询语句中的查询字段类型相匹配(包括数量),如果不匹配就会报错。
不过如果对象很多参数,岂不是要列很多,而且每个地方不一样,得多个构造函数?不能弄实体对象里面带实体这种么,非得是都是第一层的?构造函数应该也可以,传进去,构造函数里面处理,设置对象
不过话说回来分页的话,也不需要那么多字段
不过对应的实体类上还是需要manytoone,这种来关联表
3.jpql方式2 使用projection接口做映射与投影
public interface UserProjection {
String getName();
@Value("#{target.emailColumn}")//当别名与该getXXX名称不一致时,可以使用该注解调整
String getEmail();
String getDepartmentName();
Integer getRoleNum();
}
//故意将email别名为emailColumn,以便讲解@Value的用法
@Query(value = "select u.name as name, u.email as emailColumn, d.name as departmentName, count(r.id) as roleNum from User u "
+ "left join u.department d left join u.roles r group by u.id")
Page
这种方式的对比上面的好处是不用在代码里写new,但是需要通过返回接口的方式,感觉怪怪的?
https://blog.csdn.net/qq_36144258/article/details/80298354
map方式的返回:
/** * HQL通过旅店名称查询旅店以及城市的所有信息 * * @return */ @Query(value = "select new map(t1,t2) from TCity t1 left join THotel t2 on t1.id=t2.city where t2.name =:name") List
4.原生sql方式
//展示原生查询
@Query(value = "select u.name as name, u.email as emailColumn, d.name as departmentName, count(ur.role_id) as roleNum from user u "
+ "left join department d on d.id=u.department_id left join user_role ur on u.id=ur.user_id group by u.id limit :start,:size",
nativeQuery = true)
List
默认返回是list
Pageable pageable = new PageRequest(0,5);
//如果需要的话,自行封装分页信息
Page
当你需要进行sql优化时,可能用原生sql方式会更好。但是一般需求时候用JPQL还是比较方便的,毕竟这样比较省事,拿数据总是需要分页的,有时候只需要拿几个字段也是这样。
参考:https://blog.csdn.net/qq_36144258/article/details/80298354
/** * 通过旅店名称分页查询旅店以及城市的信息 * * @param name 旅店名称 * @param pageable 分页信息 * @return Page
5.集映射定义
map result
https://www.v2ex.com/t/333899
6.改为原生方式,直接写,entitimanager
参考:https://blog.csdn.net/qq_36144258/article/details/80298354
@Autowired @PersistenceContext private EntityManager entityManager; @Test public void testDynamic() throws Exception { String sql = "select new pers.zpw.domain.CityHohel(t1.name AS cityName,t2.name AS hotelName) from TCity t1 left join THotel t2 on t1.id=t2.city where t2.name ='酒店'"; Query query = entityManager.createQuery(sql); List resultList = query.getResultList(); Assert.assertTrue(resultList.size() > 0); }
7.还有一种,自己写方法,讲方位的object对象转成对应对象
https://blog.csdn.net/pp_fzp/article/details/80530588
8.映射成实体,这种返回是不错,不过要加在entity上面一大坨,感觉不好,别人2010年就写了,佩服
http://www.blogjava.net/jesson2005/articles/380884.html
补充:返回的object数组是 比如 object[0]学生实体 加 object[1] 班级的名称(下面代码如下),比如是object[0]学生实体 加 object[1]班级实体(修改例子中,entities 多个就行)
https://docs.oracle.com/javaee/5/api/javax/persistence/SqlResultSetMapping.html
参考:https://blog.jooq.org/tag/sqlresultsetmapping/
这种方式,上个链接说了不好,另外从上面这个链接读到另外一篇文章,那个家伙一样,对jpa这种方式也是很不喜欢,觉得还是sql的好,https://blog.jooq.org/2013/11/13/popular-orms-dont-do-sql/
这两种都可以实现。
总结:
1.SqlResultSetMapping 里对应的所有列要写,查询那边也需要写上所有的列,因为本来他就是找表字段对应实体关系,如果有一边缺少,就会报错。两边要完全都列上所有的。(后面发现,如果不存在列名为别名的情况,不用,直接不用写fileds,而且也不存在别名的情况,要处理。不过如果写了,就都要写全。不写就好了)
2.另外大量重复用到的话,如果某些关联是要用别名的话,将不好修改。参考文中这段话(如果是实体加某1,2个列可能存在,不过名字应该可以起的稍微不一样,如果是两个实体,重复的名字不会报错,所以应该问题也不大)
Did you notice the difference? The b.title
column was renamed to book_title
. In a SQL string. Which blows up at run time! How to remember that you have to also adapt
1 |
|
… to be
1 |
|
Conversely, how to remember that once you rename the column
in your @FieldResult
, you’ll also have to go check wherever this "BookAuthorMapping"
is used, and also change the column names in those queries.
3.另外很大一坨,而且可能要加很多坨,因为不同的地方返回的不一样,特别是大量多表链接的话。(fields可以省略,如果不需要额外其别名的话,一般也不用)
代码如下:
@SqlResultSetMapping (
name="StudentResults",
entities = {
@EntityResult(entityClass = DemoStudent.class,fields={
@FieldResult(name="studentGuid",column = "student_guid"),
@FieldResult(name="name",column = "name"),
@FieldResult(name="age",column = "age"),
@FieldResult(name="sex",column = "sex"),
@FieldResult(name="birthday",column = "birthday"),
@FieldResult(name="classGuid",column = "class_guid"),
@FieldResult(name="isDeleted",column = "is_deleted")
/*@FieldResult(name="className",column = "class_name") 直接想写在这里,直接把班级表的名称映射到这个实体类的transient字段中,是不行的,必须是数据库的,不报错,但是也不会填充值*/
})
},
columns = {
@ColumnResult(name="class_name")
}
)
@Entity
@DynamicInsert (true)
@DynamicUpdate (true)
@Table (name = "Demo_Student")
@Where (clause="Is_Deleted=0")
public class DemoStudent implements java.io.Serializable{
private static final long serialVersionUID = -1L;
private String studentGuid;
private String name;
private String age;
private String sex;
private Date birthday;
/* private DemoClass demoClass;*/
private String classGuid;
private String className;
private Boolean isDeleted;
public DemoStudent(){
}
public DemoStudent(
String studentGuid
){
this.studentGuid = studentGuid;
}
@Id
@GeneratedValue (generator = "pk")
@GenericGenerator (name = "pk", strategy = "uuid2")
@Column (name = "student_guid")
public String getStudentGuid() {
return this.studentGuid;
}
public void setStudentGuid(String value) {
this.studentGuid = value;
}
@Column (name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column (name = "age")
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Column (name = "sex")
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Column (name = "birthday")
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
/* @ManyToOne
@JoinColumn(name="class_guid",insertable = false,updatable = false)*/
/* @Transient
public DemoClass getDemoClass() {
return demoClass;
}
public void setDemoClass(DemoClass demoClass) {
this.demoClass = demoClass;
}*/
@Column (name = "class_guid")
public String getClassGuid() {
return classGuid;
}
public void setClassGuid(String classGuid) {
this.classGuid = classGuid;
}
@Column (name = "is_deleted")
public Boolean getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Boolean isDeleted) {
this.isDeleted = isDeleted;
}
@Transient
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
@Repository
public class DemoStudentDaoImpl implements DemoStudentDao{
//实体管理
@Autowired
private EntityManager entityManager;
//查询工厂
private JPAQueryFactory queryFactory;
public DemoStudentDaoImpl(){
queryFactory=new JPAQueryFactory(entityManager);
}
@Override
public void pageStudentClassListDsl(PageRequest page) {
Query q = entityManager.createNativeQuery(
"select s.student_guid,s.name,s.age,s.birthday,s.is_deleted,s.sex,s.class_guid,c.class_name from demo_student s " +
"left join demo_class c on s.class_guid=c.class_guid ",
"StudentResults");
List resultList =q.getResultList();
//这里的结果就是object数组的集合,每个object数组两个元素,第一个元素是学生实体,第二个就是classname对应的字符串
return null;
}
}
9.ConstructorResult 方式
类似
@SqlResultSetMapping( name = "view", classes = { @ConstructorResult( targetClass = TA.class, columns={ @ColumnResult(name = "name",type = String.class), @ColumnResult(name = "time",type = Date.class), @ColumnResult(name = "bname",type = String.class) } ) } )
这种,换要对应构造函数不方便。参考:https://www.v2ex.com/t/333899
6.弄成视图,好了,变成单表操作了
7.QueryDSL 方式,用这种把
https://www.jianshu.com/p/5c416a780b3e
8.返回对象不好处理,改为以前的sqlquery,好处理了。。啥都能处理了,虽然有点low,不过比mapresult那种方式简单。就这样吧。
query.unwrap(SQLQuery.class).addEntity("s",DemoStudent.class).addEntity("c",DemoClass.class);
参考:https://blog.csdn.net/zhu562002124/article/details/75097682