是什么让我选择了JPA
JPA是一种规范,Hibernate是一种JPA规范的实现,感兴趣可以移步阅读此篇文章
定义Repository之后,几乎所有的单表非聚合操作,只需要写个函数名称就完成了,自动根据定义的函数名称完成查询。
使用MapStruct也可以很容易完成多对一,对对多,一对多查询,只需要简单定义一下,加个注解就能搞定,比当初学习Spring MVC时写大量的配置文件舒坦多了。
Hibernate性能好,三级缓存,支持的数据库比较多(数据库无关性好),DAO层如上文所说,非常省代码量,另外Hibernate框架也比较成熟,对数据一致性维护比较好。
相比之下,MyBatis更为轻量,入门简单,学习门槛低,自己书写SQL语句方便,SQL自己写的调优容易,我选择Hibernate主要是因为想偶尔换一换口味,找到相同问题,不一样的解决方案,拓宽视野。
Hibernate灵活性弱,数据库无关性好,用的合理了可以不依赖某个具体的SQL数据库,弊端是对原生SQL语句支持的不友好
Spring Data Jpa框架自定义查询语句返回自定义实体的解决方案
在使用Spring Data Jpa框架时,根据业务需求我们通常需要进行复杂的数据库查询,并返回我们自定义的实体类,而在该框架下,目前仅仅支持返回与数据库映射进行持久化的POJO实体。虽然在框架上我们可以使用@Query注解执行我们自定义的sql语句,但是其返回值为List
下面我们介绍一下关于在Spring Data Jpa框架下使用自定义查询语句返回自定义实体的解决方案。
解决方案一:
例如我们有如下向关联实体:
User实体:
@Entity
@Getter
@Setter
@Table(name="tab_user")
public class User extends BaseEntity implements Serializable {
@Id
@NotNull(groups = Update.class)
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
@ApiModelProperty(value = "主键")
private Long userId;
@Column(name = "username",nullable = false)
@NotBlank
@ApiModelProperty(value = "姓名")
private String username;
@Column(name = "branch_id",nullable = false)
@NotNull
@ApiModelProperty(value = "机构id")
private Long branchId;
@Column(name = "job_id",nullable = false)
@NotNull
@ApiModelProperty(value = "岗位id")
private Long jobId;
@Override
public int hashCode() {
return Objects.hash(id, username);
}
}
Job实体
package com.sgcc.modules.system.domain;
import com.sgcc.base.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;
/**
* @author: liman
* @Date: 2020/7/17 17:37
* @Description: 员工表
*/
@Entity
@Getter
@Setter
@Table(name="tab_job")
public class JobTab extends BaseEntity implements Serializable {
@Id
@Column(name = "job_id")
@NotNull(groups = Update.class)
@ApiModelProperty(value = "ID", hidden = true)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long jobId;
@Column(name = "job_name", nullable = false)
@NotBlank
@ApiModelProperty(value = "岗位名称")
private String jobName;
@Column(name = "branch_id", nullable = false)
@NotBlank
@ApiModelProperty(value = "岗位机构")
private String branchId;
@Column(name = "job_type", nullable = false)
@NotBlank
@ApiModelProperty(value = "岗位类别")
private String jobType;
}
Branch实体
package com.sgcc.modules.system.domain;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.sgcc.base.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* @website https://el-admin.vip
* @description /
* @author liman
* @date 2020-07-07
**/
@Entity
@Getter
@Setter
@Table(name="tab_branch")
public class Branch extends BaseEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@ApiModelProperty(value = "id")
private Long id;
@Column(name = "branch_id",nullable = false)
@NotNull
@ApiModelProperty(value = "机构id")
private Long branchId;
@Column(name = "branch_name",nullable = false)
@NotBlank
@ApiModelProperty(value = "机构名称")
private String branchName;
@Column(name = "level",nullable = false)
@NotNull
@ApiModelProperty(value = "级别")
private Integer level;
@Column(name = "p_id",nullable = false)
@NotNull
@ApiModelProperty(value = "父级机构id")
private Long pId;
@Column(name = "dept_sort",nullable = false)
@NotNull
@ApiModelProperty(value = "部门排序级别")
private Integer deptSort;
}
}
要转化成的实体Dto如下:
package com.sgcc.modules.system.domain.resp;
import lombok.Getter;
import lombok.Setter;
/**
* @author: liman
* @Date: 2020/7/17 16:42
* @Description: 测评对象返回类
*/
@Getter
@Setter
public class EvaUserResp {
/** 所在部门*/
private String branchName;
/** 测评对象*/
private String userName;
/** 职位*/
private String jobName;
/** 职位类别*/
private String jobType;
public EvaUserResp() {
}
public EvaUserResp(String branchName, String userName, String jobName, String jobType) {
this.branchName = branchName;
this.userName = userName;
this.jobName = jobName;
this.jobType = jobType;
}
}
下面我们来看下DAO层的JPA处理接口类
public interface BookInfoRepository extends JpaRepository {
@Query(value = "SELECT b.branch_name as branchName," +
"a.username as userName," +
"c.job_name as jobName," +
"c.job_type as jobType FROM tab_user a " +
"INNER JOIN tab_branch b ON b.branch_id = a.branch_id " +
"INNER JOIN tab_job c on c.job_id = a.job_id " +
"WHERE user_id = :userId ", nativeQuery = true)
List
我们来解释一下上面这个处理接口类中的要点:
①nativeQuery=true,属性的设置,是表明该方法中的sql以数据库的sql语句格式对待。
②返回值为List
最后我们看下将该List
public class EntityUtils {
private static Logger logger = LoggerFactory.getLogger(EntityUtils.class);
/**
* 将数组数据转换为实体类
* 此处数组元素的顺序必须与实体类构造函数中的属性顺序一致
*
* @param list 数组对象集合
* @param clazz 实体类
* @param 实体类
* @param model 实例化的实体类
* @return 实体类集合
*/
public static List castEntity(List
在执行DAO层方法,获得相应的List
在使用该解决方案时,需注意以下几点要求:
①自定义查询语句中的查询字段的顺序一定要和自定义实体的构造方法中的属性顺序一致。
②此种方案在解决特别复杂的查询语句时很高效,因为只需自定义查询语句,与数据库进行一次交互即可,效率可观。但对程序的规范性要求比较高。
③此方案在解决当前项目数据库中数据表在业务需求下创建而不符合使用JPA框架创建持久化实体之间的关联关系(即因为业务需求,所建立库表不符合数据库建库规范),而又需要进行多表关联进行复杂查询时,很实用。
特别说明:上面所举的例子只是单纯为了演示此方案,因为上面表关联之简单要获得如上的结果使用JPA框架也可轻松实现。
具体转化demo如下:
/**object对象转化EvaUserResp对象*/
List
解决方案二:
修改DAO层的JPA处理接口类:
@Query(value = "SELECT new EvaUserResp(b.branch_name as branchName,a.username as userName,c.job_name as jobName,c.job_type as jobType ) FROM tab_user a INNER JOIN tab_branch b ON b.branch_id = a.branch_id INNER JOIN tab_job c on c.job_id = a.job_id WHERE user_id = 4 ", nativeQuery = true)
List findByUserId(@Param("userId") Long userId);
注意:这次的EvaUserResp最好写全路径,程序有可能无法定位到该类
解决方案三:
我要解决这样一条sql查询出来的结果:
SELECT b.branch_name as branchName," +
"a.username as userName," +
"c.job_name as jobName," +
"c.job_type as jobType FROM tab_user a " +
"INNER JOIN tab_branch b ON b.branch_id = a.branch_id " +
"INNER JOIN tab_job c on c.job_id = a.job_id " +
"WHERE user_id = :userId
第一步,首先要引入阿里的fastJson
第二步:Repository层用List
@Query(value = "SELECT b.branch_name as branchName," +
"a.username as userName," +
"c.job_name as jobName," +
"c.job_type as jobType FROM tab_user a " +
"INNER JOIN tab_branch b ON b.branch_id = a.branch_id " +
"INNER JOIN tab_job c on c.job_id = a.job_id " +
"WHERE user_id = :userId ", nativeQuery = true)
List
第三步进行转化:
List
更多方案持续更新中......