1.基本说明
JPA方言问题,是在进行JPA开发时,可能遇到的问题。主要遇到问题的场景为:JPA由于要适应不同数据库,而不同数据库表结构和数据类型存在的一定的区别,从而造成方言问题。
JPA的方言问题的表现主要为:JAVA之中的Bean类与SQL语句转换过程中,存在转化失败的问题。
JPA方言的最可能出问题的地方在调用原生的SQL语句时,此时由于导出的表类型与Java的Bean类型不一致,造成设置类型错误而无法运行。
2.基本解决方案:persistence.xml文件修改
更改persistence.xml文件,是解决问题的基本思路。不同数据库,对应不同的方言;而且,不同的数据库有不同的官方方言库。
官方库或官方类对应的参数配置为hibernate.dialect,建议SQL Server选择SQLServer2012Dialect,MySQL选择MySQL8Dialect。
具体样例参考下面所示。
<!--定义数据库连接的相关属性 -->
<!-- <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2012Dialect" />-->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect" />
<!-- <property name="hibernate.dialect" value="org.hibernate.dialect.SybaseDialect" />--
3.解决方案1:建造适合新数据的Bean
如果已经使用了官方Dialect,在调用官方导出类的适合,一般不会出问题。真正出问题可能在使用原生的SQL语句的时候。当JAVA调用原生的SQL语句时,可能会有导出的数据类型和JAVA类的Bean类型不一致,造成类型设置错误。
错误样例如下:
String sql = “SQL语句”;
NativeQueryImpl queryImpl = em.createNativeQuery(sql).unwrap(NativeQueryImpl.class);
queryImpl.setResultTransformer(Transformers.aliasToBean(A.class));
List<A> db = queryImpl.getResultList();
如果A中的某一变量或属性和导出数据不匹配,会造成设置数据错误。
更改方法如下:
解决方案为,对原有类进行复制,建立ACopy类,对发生错误的属性进行setter函数更改。
代码如下:
public class A {
//在某些数据库,如MySQL中就会出问题,因为用原生的SQL,导出的数据库表的类型为Byte
public Boolean key;
public Boolean getKey() {
return key;
}
public void setKey(Boolean key) {
this.key = key;
}
}
public class ACopy {
//在某些数据库,如MySQL中就会出问题,因为用原生的SQL,导出的数据库表的类型为Byte
public Boolean key;
public Boolean getKey() {
return key;
}
public void setKey(Byte key) {
this.key = key;
}
}
//执行如下的JAVA语句
EntityManagerFactory emf = null; //自己构造
EntityManager em = emf.createEntityManager();
NativeQueryImpl queryImpl = em.createNativeQuery(sql).unwrap(NativeQueryImpl.class);
if(emf.getProperties().get("hibernate.connection.url").toString().toLowerCase().contains("mysql")) {
queryImpl.setResultTransformer(Transformers.aliasToBean(ACopy.class));
} else {
queryImpl.setResultTransformer(Transformers.aliasToBean(A.class));
}
List<A> iquery = queryImpl.getResultList();
4.解决方案2:使用Stream方法Map
第二种解决方案时直接用导出的类,调用CriteriaBuilder等语句,生成表。然后,用Stream方法生成类和Map,进行Map映射。
此种方法的缺点为:必须保证Map下的Key和Value是唯一的,否则会造成数据错误。
样例如下(仅作Map说明):
List<ObjA> lstA = new LinkedList<>();
lstA.add(new ObjA("01", "王"));
lstA.add(new ObjA("02", "李"));
lstA.add(new ObjA("03", "张"));
lstA.add(new ObjA("04", "刘"));
List<ObjB> lstB = new LinkedList<>();
lstB.add(new ObjB("01", "Wang"));
lstB.add(new ObjB("02", "Li"));
lstB.add(new ObjB("03", "Zhang"));
lstB.add(new ObjB("04", "Liu"));
Map<String, ObjA> mapA = lstA.stream().collect(Collectors.toMap(ObjA::getId, t -> t));
System.out.println(mapA);
List<ObjC> lstC = new LinkedList<>();
for(ObjB objB: lstB){
lstC.add(new ObjC(objB.id, mapA.get(objB.id).name, objB.code));
}
System.out.println("test");