大致结构:

Person(人): id,name,age,bookId

Book(书):id,bookName

Author(作者):id,authorName,bookId

一个人 只有 一本书,一本书 有多个 作者,一个作者 只出 一本书;(可能举例不好,明白就行)

Person ----> Book : OneToOne

Book ----> Author: OneToMany

期望达到的效果:

sql: select p.id id,p.name,b.bookName bookName from person p left join book b on p.bookId=b.id ;

不管这个person有没有book都要列出这个person

如果是规范的hibernate实体对象(不知道怎么描述规范,大致是符合hibernate面向对象的表设计的实体类)

sql相应的hql: select p.id id,p.name,b.bookName bookName from person p left join p.book b with p.bookId=b.id ; (有很多写法,但如果hibernate映射,我查到的说法是无法用hql写left join)

@Entity
@Table
public class Person{
private String id;
private String name;
private String age;
@OneToOne
private Book book; //...
}
@Entity
@Table
public class Book {
private String id;
private String bookName;
@OneToOne
private Person person;
@OneToMany
private List<Author> authors;
//...
}
@Entity
@Table
public class Author{
private String id;
private String authorName;
@ManyToOne
private Book book; //...
}

如果是这样,其实得到person就可以得到person的所有关联对象。但,如果考虑极致的查询效率、内存占用。那么(个人)感觉这做法不好。

较好的做法是,你要什么sql就返回什么指定的列映射到dto(所以,个人还是喜欢用ibatis/mybatis);

而且,现在遇到的问题是。实体对象Perosn中 private Book book; 写成了 private String bookId;导致hql无法达到left的效果(在没映射的关系下,反正没找到能达到left join效果的hql)。

所以,就用hibernate的sql来实现。(也可以用jdbcTmplate.query(…),只是分页是自己sql写好的;返回到自定义对象只要实现RowMapper就可以)

Demo:

String sql = "select p.id id ,p.name name ,b.bookName bookName from person p left join book b on p.bookId=b.id";
SQLQuery q = ....;
//q.addScalar("id",StringType.INSTANCE);
//q.addScalar("name",StringType.INSTANCE);
//q.addScalar("bookName",StringType.INSTANCE);
q.setResultTransformer(Transformers.aliasToBean(Dto.class));

问题:

用的数据库是oracle,所以导致默认获得到的别名alias都转成了大写。但是,在dto中又是驼峰式的。

所以,在Transformers中会报exception:can not find setter;

因为,Transformers是根据alias反射找到setter。但alias都是大写ID、NAME、BOOKNAME,但setter其实是setId、setName、setBookName。

解决:

1、sql中可以写成  select p.id “id” ,p.name “name” ,b.bookName “bookName” from person p left join book b on p.bookId=b.id ; 即sql中alias都加上双引号(oracle的规定),那么Transformers得到的alias不会被转换成大写。

2、设置addScalar(…),但个人觉得写的太多而且不通用,每个都要写。(最好加上类型)

3、重写/扩展oracle的dialect;

4、我也在博问发求助了:Hibernate原生sql查询多表返回自定义对象问题? ,又大致去查了下,在stackoverflow找到一个:mapping Hibernate query results to custom class? 重点看2L v.ladynev的回答,并且ta给了ta自己重写的transformers fluent-hibernate

其实ta的做法和我在看transformers源码时想到的一样(本来以为代码量很少),既然是alias反射dto是找不到setter,那么就想办法找到setter;

public Object transformTuple(Object[] tuple, String[] aliases) {
Object result;
try {
if ( ! isInitialized ) {
initialize( aliases ); //exception
}
else {
check( aliases );
}
result = resultClass.newInstance();
for ( int i = 0; i < aliases.length; i++ ) {
if ( setters[i] != null ) {
setters[i].set( result, tuple[i], null );
}
}
}
catch ( InstantiationException e ) {
throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
}
catch ( IllegalAccessException e ) {
throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
}
return result;
} private void initialize(String[] aliases) {
PropertyAccessor propertyAccessor = new ChainedPropertyAccessor(
new PropertyAccessor[] {
PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
PropertyAccessorFactory.getPropertyAccessor( "field" )
}
);
this.aliases = new String[ aliases.length ];
setters = new Setter[ aliases.length ];
for ( int i = 0; i < aliases.length; i++ ) {
String alias = aliases[ i ];
if ( alias != null ) {
this.aliases[ i ] = alias;
setters[ i ] = propertyAccessor.getSetter( resultClass, alias ); //exception: alias都是大写,再深入看propertyAccessor.getSetter就知道是找不到setter
}
}
isInitialized = true;
}
// org.hibernate.property.BasicPropertyAccessor
private static Method setterMethod(Class theClass, String propertyName) { //propertyName就是alias,大写
BasicPropertyAccessor.BasicGetter getter = getGetterOrNull(theClass, propertyName);
Class returnType = getter == null?null:getter.getReturnType();
Method[] methods = theClass.getDeclaredMethods();
Method potentialSetter = null;
Method[] arr$ = methods;
int len$ = methods.length; for(int i$ = 0; i$ < len$; ++i$) {
Method method = arr$[i$];
String methodName = method.getName();
if(method.getParameterTypes().length == 1 && methodName.startsWith("set")) {
String testStdMethod = Introspector.decapitalize(methodName.substring(3));
String testOldMethod = methodName.substring(3); // 都是dto的setter值,Id、Name、BookName
if(testStdMethod.equals(propertyName) || testOldMethod.equals(propertyName)) { // 所以,此处if为false;method=null
potentialSetter = method;
if(returnType == null || method.getParameterTypes()[0].equals(returnType)) {
return method;
}
}
}
} return potentialSetter;
}
// 这是fluent-hibernate的源码 ;com.github.fluent.hibernate.internal.util.reflection.ReflectionUtils
/**
* Try to find a class getter method by a property name. Don't check parent classes or
* interfaces.
*
* @param classToCheck
* a class in which find a getter
* @param propertyName
* a property name
* @return the getter method or null, if such getter is not exist
*/
public static Method getClassGetter(Class<?> classToCheck, String propertyName) {
PropertyDescriptor[] descriptors = getPropertyDescriptors(classToCheck); for (PropertyDescriptor descriptor : descriptors) {
if (isGetter(descriptor, propertyName)) {
return descriptor.getReadMethod();
}
} return null;
} private static boolean isGetter(PropertyDescriptor descriptor, String propertyName) {
Method method = descriptor.getReadMethod();
return method != null && method.getParameterTypes().length == 0
&& descriptor.getName().equalsIgnoreCase(propertyName); //忽略大小写找到setter
} private static PropertyDescriptor[] getPropertyDescriptors(Class<?> beanClass) {
try {
return Introspector.getBeanInfo(beanClass).getPropertyDescriptors();
} catch (IntrospectionException ex) {
throw InternalUtils.toRuntimeException(ex);
}
}

我并没去完整认真的去看fluent-hibernate,可能是因为自己也是想通过找到setter的去解决 + 作者回答时候也是说主要就是怎么找到setter,把BookName当作BOOKNAME看待:

Using a custom result transformer

Another way to solve the problem — using a result transformer that ignores method names case (treat getFirstName() as getFIRSTNAME()). You can write your own or use FluentHibernateResultTransformer. You will not need to use quotes and aliases (if you have column names equal to DTO names)

ps: 其实想看认真看下的,本来我想的不用写多少代码量。但发现作者写了好多…

尚存疑问:

我记得我还看到的说法有说重写或扩展oralce的dialect的。记得之前公司的项目框架有重写oracle的dialect,貌似sql中的别名不会被转换成全大写。

具体不清楚,整天都是在写垃圾的业务实现代码,就知道叫加班,代码一点质量都没有,不好看懂、不好扩展、方法老旧,只是为了实现当前业务需求应付客户。搞的下班了没时间、也没经理去深入一些框架的东西,烦躁…><!

【Hibernate】hibernate原生sql利用transformers返回多表自定义类型对象的更多相关文章

  1. Hibernate SQLQuery 原生SQL 查询及返回结果集处理-1

    第一篇:官方文档的处理方法,摘自官方 在迁移原先用JDBC/SQL实现的系统,难免需要采用hibernat native sql支持. 1.使用SQLQuery hibernate对原生SQL查询执行 ...

  2. Hibernate运行原生sql并将查询的结果转化为对象

    原生SQL查询执行的控制是通过SQLQuery接口进行的,通过执行Session.createSQLQuery()获取这个接口.下面来描述如何使用这个API进行查询.标量查询(Scalar queri ...

  3. Hibernate SQLQuery 原生SQL 查询及返回结果集处理-2

    1. 返回List, .setResultTransformer(      Transformers.ALIAS_TO_ENTITY_MAP);将结果转为Map,存放到list中,即list中为若干 ...

  4. Hibernate执行原生SQL返回List<Map>类型结果集

    我是学java出身的,web是我主要一块: 在做项目的时候最让人别扭的就是hibernate查询大都是查询出List<T>(T指代对应实体类)类型 如果这时候我用的联合查询,那么返回都就是 ...

  5. hibernate使用原生SQL查询返回结果集的处理

    今天没事的时候,看到公司框架里有一个用原生SQL写的函数,说实在以前自己也干过这事,但好久都没有用,都忘得差不多了,现在基本都是用的hql语句来查询结果.hibernate中使用createSQLQu ...

  6. java:Hibernate框架3(使用Myeclipse逆向工程生成实体和配置信息,hql语句各种查询(使用hibernate执行原生SQL语句,占位符和命名参数,封装Vo查询多个属性,聚合函数,链接查询,命名查询),Criteria)

    1.使用Myeclipse逆向工程生成实体和配置信息: 步骤1:配置MyEclipse Database Explorer: 步骤2:为项目添加hibernate的依赖: 此处打开后,点击next进入 ...

  7. hibernate 5原生sql查询测试学习代码

    基本查询 import java.util.List; import org.hibernate.SQLQuery; import org.hibernate.Session; import org. ...

  8. hibernate在使用sql查询query自动转化成model类型数据,query.addEntity

    hibernate使用自动的hql查询或者其封装的查询方法都能字段转化成对象 而如果在hibernate中使用sql时大多返回为Object[]对象 那么如何将object[]转换成model呢,答案 ...

  9. hibernate使用原生SQL查询

    以下是Demo测试Hibernate 原生SQL查询: import java.util.Iterator; import java.util.List; import java.util.Map; ...

随机推荐

  1. Python爬虫小结

    有些数据是没有专门的数据集的,为了找到神经网络训练的数据,自然而然的想到了用爬虫的方法开始采集数据.一开始采用了网上的一个动态爬虫的代码,发现爬取的图片大多是重复的,有效图片很少. 动态爬虫: fro ...

  2. 2018icpc徐州网络赛-H Ryuji doesn't want to study(线段树)

    题意: 有n个数的一个数组a,有两个操作: 1 l r:查询区间[l,r]内$a[l]*(r-l+1)+a[l+1]*(r-l)+a[l+2]*(r-l-1)+\cdots+a[r-1]*2+a[r] ...

  3. 概念验证:在Kubernetes中部署ABAP

    对于将SAP ABAP应用服务器组件容器化和在Kubernetes中部署它们,我们在SPA LinuxLab中做了概念验证(PoC),本文将介绍一些我们的发现和经验.本文会也会指出这项工作的一些潜在的 ...

  4. 量子计算机编程(一)——QPU编程

    今天要给大家介绍的是这本书<Programming Quantum Computers -- Essential Algorithms and Code Samples>,主要讲如何在量子 ...

  5. Shiro权限验证说明

    1.简介 shiro是一个安全框架,是Apache的一个子项目.shiro提供了:认证.授权.加密.会话管理.与web集成.缓存等模块. 1.1.模块介绍 Authentication:用户身份识别, ...

  6. Unity酱~ 卡通渲染技术分析(二)

    前面的话 上一篇Unity酱~ 卡通渲染技术分析(一) 写了CharaMain.cginc,服装的渲染是怎么实现的.这篇来分析一下头发跟皮肤的实现 头发 本来以为unitychan的头发会有各向异性的 ...

  7. postman之批量数据参数化(文件)

    相信小伙伴们在做接口测试时需要导入大量的数据进行测试,Jmeter进行接口测试时可以导入CSV数据文件进行参数化,那么postman又该如何导入数据文件进行测试呢?下面我给大家讲解一下. 第一:创建t ...

  8. 《Head first设计模式》之单例模式

    单例模式(书中叫单件模式,个人习惯叫单例)确保一个类只有一个实例,并提供一个全局访问点. 有一些对象我们只需要一个,比方说:线程池.缓存.对话框.处理器偏好设置和注册表的对象等等.事实上,这类对象只能 ...

  9. javascript检测客户端环境是否是pc端

    //isPC(): 检测客户端环境是否是pc端 function isPC(){ let userAgent = navigator.userAgent; let agents = ["An ...

  10. 中小企业自建云WAF有多难?只需20分钟!而且:全程免费!

    以往,运营型的web为了安全目的,才使用WAF进行安全防护. 而现如今,WAF对企业web来说,已然成了刚需.为何?等保.网络安全法的硬性要求! 当然,这样要求显然是对的:没有网络安全,就没有国家安全 ...