JPAを選択した理由
JPAは仕様であり、HibernateはJPA仕様の実装です。興味がある場合は、この記事を読むために移動できます。
リポジトリを定義した後、ほとんどすべての単一テーブルの非集計操作で関数名を記述するだけで済みます。これが実行され、定義された関数名に従ってクエリが自動的に完了します。
MapStructを使用すると、多対1、ペア対多、および1対多のクエリを簡単に完了することもできます。簡単に定義して注釈を追加するだけで完了します。SpringMVCを学習するときに、多数の構成ファイルを作成するよりもはるかに快適です。
Hibernateは、優れたパフォーマンスと3レベルのキャッシュを備え、より多くのデータベースをサポートします(データベースの独立性が優れています)。前述のように、DAOレイヤーはコードを大幅に節約します。さらに、Hibernateフレームワークは比較的成熟しており、データの一貫性をよりよく維持します。
対照的に、MyBatisはより軽量で、使い始めやすく、学習しきい値が低く、自分でSQLステートメントを記述しやすく、自分で記述したSQLを調整しやすいです。主に、時々好みを変えて同じ問題を見つけたいという理由で、Hibernateを選択します。同じ解決策が視野を広げます。
Hibernateは柔軟性が低く、データベースの独立性が良好です。適切に使用すると、特定のSQLデータベースに依存できません。欠点は、ネイティブSQLステートメントのサポートに対応していないことです
。SpringDataJpaフレームワークのカスタムクエリステートメントは、カスタムエンティティソリューションを返します。
Spring Data Jpaフレームワークを使用する場合、通常、ビジネス要件に基づいて複雑なデータベースクエリを実行し、カスタムエンティティクラスを返す必要があります。このフレームワークでは、現在、永続化のためにデータベースにマップされているPOJOエンティティの返送のみをサポートしています。@Queryアノテーションを使用して、フレームワークでカスタムSQLステートメントを実行できますが、戻り値は、複数のオブジェクト配列のリストコレクションであるList <Object []>タイプです。
以下では、Spring DataJpaフレームワークでカスタムエンティティを返すためにカスタムクエリステートメントを使用するソリューションを紹介します。
解決策1:
たとえば、次の関連エンティティがあります。
ユーザーエンティティ:
@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);
}
}
ジョブエンティティ
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;
}
ブランチエンティティ
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<BookInfo, BigDecimal> {
@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<Object[]>findByUserId(@Param("userId") Long userId);
}
上記の処理インターフェースクラスの要点を説明しましょう。
①nativeQuery= trueの場合、属性設定は、このメソッドのsqlがデータベースのsqlステートメント形式で処理されることを示します。
②戻り値はList <Object []>です。Jpaはクエリ結果のカスタムエンティティへのマッピングを自動的に完了できないと述べたため、受信するには変更されたオブジェクトを使用する必要があります。
最後に、List <Object []>オブジェクトをカスタムエンティティに変換するツールクラスを見てみましょう。
public class EntityUtils {
private static Logger logger = LoggerFactory.getLogger(EntityUtils.class);
/**
* 将数组数据转换为实体类
* 此处数组元素的顺序必须与实体类构造函数中的属性顺序一致
*
* @param list 数组对象集合
* @param clazz 实体类
* @param <T> 实体类
* @param model 实例化的实体类
* @return 实体类集合
*/
public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz, Object model) {
List<T> returnList = new ArrayList<T>();
if (list.isEmpty()) {
return returnList;
}
//获取每个数组集合的元素个数
Object[] co = list.get(0);
//获取当前实体类的属性名、属性值、属性类别
List<Map> attributeInfoList = getFiledsInfo(model);
//创建属性类别数组
Class[] c2 = new Class[attributeInfoList.size()];
//如果数组集合元素个数与实体类属性个数不一致则发生错误
if (attributeInfoList.size() != co.length) {
return returnList;
}
//确定构造方法
for (int i = 0; i < attributeInfoList.size(); i++) {
c2[i] = (Class) attributeInfoList.get(i).get("type");
}
try {
for (Object[] o : list) {
Constructor<T> constructor = clazz.getConstructor(c2);
returnList.add(constructor.newInstance(o));
}
} catch (Exception ex) {
logger.error("实体数据转化为实体类发生异常:异常信息:{}", ex.getMessage());
return returnList;
}
return returnList;
}
/**
* 根据属性名获取属性值
*
* @param fieldName 属性名
* @param modle 实体类
* @return 属性值
*/
private static Object getFieldValueByName(String fieldName, Object modle) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = "get" + firstLetter + fieldName.substring(1);
Method method = modle.getClass().getMethod(getter, new Class[]{});
Object value = method.invoke(modle, new Object[]{});
return value;
} catch (Exception e) {
return null;
}
}
/**
* 获取属性类型(type),属性名(name),属性值(value)的map组成的list
*
* @param model 实体类
* @return list集合
*/
private static List<Map> getFiledsInfo(Object model) {
Field[] fields = model.getClass().getDeclaredFields();
List<Map> list = new ArrayList(fields.length);
Map infoMap = null;
for (int i = 0; i < fields.length; i++) {
infoMap = new HashMap(3);
infoMap.put("type", fields[i].getType());
infoMap.put("name", fields[i].getName());
infoMap.put("value", getFieldValueByName(fields[i].getName(), model));
list.add(infoMap);
}
return list;
}
}
DAOレイヤーメソッドを実行した後、対応するList <Object []>オブジェクトを取得し、ツールクラスの静的メソッドcastEntityを呼び出して、データをカスタムエンティティに変換します。
このソリューションを使用するときは、次の要件に注意してください。
①カスタムクエリステートメントのクエリフィールドの順序は、カスタムエンティティの構築方法の属性の順序と一致している必要があります。
②このソリューションは、クエリステートメントをカスタマイズしてデータベースを1回操作するだけで済み、効率が高いため、特に複雑なクエリステートメントを解決する場合に非常に効率的です。しかし、手順の規範的な要件は比較的高いです。
③このソリューションは、現在のプロジェクトデータベースのデータテーブルがビジネス要件の下で作成され、永続エンティティを作成するためのJPAフレームワークの使用間の関連付け関係に準拠していない(つまり、ビジネス要件のために、確立されたデータベーステーブルがデータベース構築仕様に準拠していない)という問題を解決します。複雑なクエリに対して複数のテーブルの関連付けを実行する必要がある場合は、非常に実用的です。
特記事項:上記の例は、このスキームを示すためのものです。上記のテーブルの関連付けは、上記の結果を取得するのが簡単であり、JPAフレームワークを使用して簡単に実装できるためです。
具体的な変換デモは次のとおりです。
/**object对象转化EvaUserResp对象*/
List<Object[]> evaUserResp = userRepository.findByUserId(x.getUserId());
List<EvaUserResp> evaUserResps = EntityUtils.castEntity(evaUserResp, EvaUserResp.class, new EvaUserResp());
解決策2:
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<EvaUserResp> findByUserId(@Param("userId") Long userId);
注:今回はEvaUserRespがフルパスを記述するのに最適であり、プログラムはこのカテゴリを見つけることができない場合があります
解決策3:
我要解决这样一条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を紹介することです
<dependency>
<groupId> com.alibaba </ groupId>
<artifactId> fastjson </ artifactId>
<version> 1.2.56 </ version>
</ dependency>
ステップ2:リポジトリレイヤーはList <Map <String、String >>を使用して返されたデータを受信します
@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<Map<String,String>> findByUserId(@Param("userId") Long userId);
3番目のステップは変換することです:
List<Map<String,String>> evaUserResp = userRepository.findByUserId(x.getUserId());
String irsStr = JSON.toJSONString(evaUserResp);
List<EvaUserResp> evaUserResps = JSON.parseArray(irsStr,EvaUserResp.class);
より多くの計画が更新され続けています...