Spring Data Jpa 動態表處理
Jpa 分表問題
現在有一張學生表t_stu按年份進行了處理,物理表分別是t_stu_2020、t_stu_2021、t_stu_2022這樣
如果是mybatis,可以直接把表后綴傳入sql,然后使用t_stu_${year}對表名進行拼接
但是對于Jpa這個全自動化ORM框架來說不太好處理,因為Jpa的表名是寫在實體類的注解上的,在運行時不能修改:
// 實體類
@Entity
@Table(name = "t_stu") // 這里的表名定義是無法在運行時修改的
@Getter
@Setter
public class TVisionStu {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id")
private String id;
@Column(name = "name")
private String name;
}
// dao 層
@Repository
public interface TVisionStuDao extends JpaRepository<TVisionStu, String>, JpaSpecificationExecutor<TVisionStu> {}
解決方案
Hibernate 插件方式
Hibernate 插件可以在sql執行前對sql進行修改,從而達到動態表的功能
import org.apache.commons.lang3.StringUtils;
import org.hibernate.EmptyInterceptor;
import org.springframework.stereotype.Component;
@Component
public class HibernateInterceptor extends EmptyInterceptor {
@Override
public String onPrepareStatement(String sql) {
String schoolYear = ReqContextHolder.getSchoolYear();
// 這里對表名進行替換
if (StringUtils.isNotBlank(schoolYear)) {
return sql.replaceAll("t_stu", "t_stu_" + schoolYear);
}
return super.onPrepareStatement(sql);
}
}
其中的ReqContextHolder是一個線程安全的類,用來保存要查找的實際年份
public class ReqContextHolder {
private ReqContextHolder() {
}
protected static ThreadLocal<String> schoolYearThreadLocal = new ThreadLocal<>();
public static void setSchoolYear(String schoolYear) {
schoolYearThreadLocal.set(schoolYear);
}
public static String getSchoolYear() {
return schoolYearThreadLocal.get();
}
public static void remove() {
schoolYearThreadLocal.remove();
}
}
然后在application.yml中對插件進行配置:
spring:
設置jpa插件,可以實現分表
jpa:
properties:
hibernate:
ejb:
# 注冊自定義的插件類
interceptor: com.power.aop.HibernateInterceptor
實際使用的話就像這樣:
@RestController
@RequestMapping("/stu")
public class StuController{
@GetMapping("/query")
public List<TVisionStu> query(@RequestParam("schoolYear") String schoolYear) {
ReqContextHolder.setSchoolYear(schoolYear);
List<TVisionStu> list = this.stuDao.findAll();
ReqContextHolder.remove();
return list;
}
}
然后就可以到sql可以正常執行了。
如果覺得每個方法都需要設置學年非常麻煩,可以寫一個攔截器進行處理,這里就不再贅述了。