背景
在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
未验证
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论