背景
在jpa查询中,有时只需要查部分字段,这时jpa repository查出的是map,无法映射到entity类。
会提示错误:
org.springframework.core.convert.converternotfoundexception:
no converter found capable of converting from type
[org.springframework.data.jpa.repository.query.abstractjpaquery$tupleconverter$tuplebackedmap] to type
网上搜索有多种解决方案。这里列举一下。
经过验证,本人采取了第一种方案,证明是可行的。
jpa 2.1以上的解决办法
实体中增加named query和result map
@sqlresultsetmapping(name = "ebookinfo",
classes = @constructorresult(targetclass = ebookinfo.class,
columns = {
@columnresult(name = "book_id", type = long.class),
@columnresult(name = "book_name", type = string.class),
@columnresult(name = "file_type", type = string.class)
}))
@namednativequery(name = "listexpressebooks",
query = "select book_id, book_name, file_type from ebook order by update_date desc",
resultsetmapping = "ebookinfo")
@entity
@table(name = "ebook")
public class ebook {
private long bookid;
private integer authorid;
private string authorname;
private integer categoryid;
private string bookname;
private string subtitle;
private string tags;
private string isbn;
private string edition;
private byte booktype;
private integer star;
private integer downloadcount;
private byte status;
private string filetype;
private string outline;
private string introduction;
private string preface;
private string cover;
private float price;
private string publisher;
private string bgcolor;
private string forecolor;
private string titlecolor;
private string coverbackgroundid;
private string coverpictureid;
private integer covertemplateid;
private string coverpicturemode;
private integer pagemode;
private timestamp requestdate;
private timestamp publishdate;
private timestamp updatedate;
定义一个新的dto对象
字段和查询的字段对应,需要提供构造函数:
@data
public class ebookinfo {
private long bookid;
private string bookname;
private string filetype;
public ebookinfo(long bookid, string bookname, string filetype) {
this.bookid = bookid;
this.bookname = bookname;
this.filetype = filetype;
}
}
repository中定义查询接口
@query(name = "listexpressebooks", nativequery = true)
public list<ebookinfo> listexpressebooks();其它方案
查询中构造新对象
public list<blog> selectbyyearmonth(string year, string month, int status) {
string sql = string.format("select new blog(blog.id, blog.title, blog.abs, blog.createtime) from blog blog where blog.status = %d and year(createtime) = %s and month(createtime) = %s order by blog.createtime desc", status, year, month);
//query query = this.em.createnativequery(sql, "expressedresult");
query query = this.em.createquery(sql);
list results = query.getresultlist();
return results;
}上述方法是之前我项目中代码库里的写法,blog需要提供相应的构造函数。
自己写convertor
repository 返回 tuple 对象,自己写代码手动转换为指定对象,repository层使用native查询。
这里要借助辅助类:
class nativeresultprocessutils {
/**
* tuple转实体对象
* @param source tuple对象
* @param targetclass 目标实体class
* @param <t> 目标实体类型
* @return 目标实体
*/
public static <t> t processresult(tuple source,class<t> targetclass) {
object instantiate = beanutils.instantiate(targetclass);
converttupletobean(source,instantiate,null);
return (t) instantiate;
}
/**
*
* tuple转实体对象
* @param source tuple对象
* @param targetclass 目标实体class
* @param <t> 目标实体类型
* @param ignoreproperties 要忽略的属性
* @return 目标实体
*/
public static <t> t processresult(tuple source,class<t> targetclass,string... ignoreproperties) {
object instantiate = beanutils.instantiate(targetclass);
converttupletobean(source,instantiate,ignoreproperties);
return (t) instantiate;
}
/**
* 把tuple中属性名相同的值复制到实体中
* @param source tuple对象
* @param target 目标对象实例
*/
public static void converttupletobean(tuple source,object target){
converttupletobean(source,target,null);
}
/**
* 把tuple中属性名相同的值复制到实体中
* @param source tuple对象
* @param target 目标对象实例
* @param ignoreproperties 要忽略的属性
*/
public static void converttupletobean(tuple source,object target, string... ignoreproperties){
//目标class
class<?> actualeditable = target.getclass();
//获取目标类的属性信息
propertydescriptor[] targetpds = beanutils.getpropertydescriptors(actualeditable);
//忽略列表
list<string> ignorelist = (ignoreproperties != null ? arrays.aslist(ignoreproperties) : null);
//遍历属性节点信息
for (propertydescriptor targetpd : targetpds) {
//获取set方法
method writemethod = targetpd.getwritemethod();
//判断字段是否可以set
if (writemethod != null && (ignorelist == null || !ignorelist.contains(targetpd.getname()))) {
//获取source节点对应的属性
string propertyname = targetpd.getname();
object value = source.get(propertyname);
if(value!=null && classutils.isassignable(writemethod.getparametertypes()[0], value.getclass())) {
try {
//判断target属性是否private
if (!modifier.ispublic(writemethod.getdeclaringclass().getmodifiers())) {
writemethod.setaccessible(true);
}
//写入target
writemethod.invoke(target, value);
}
catch (throwable ex) {
throw new fatalbeanexception(
"could not copy property '" + targetpd.getname() + "' from source to target", ex);
}
}
}
}
}
}
使用entitymanager的transformers.aliastobean
未验证,spring data jpa未必支持
使用entitymanager的transforms.alias_to_entity_map
未验证
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论