152

我有一个带有本机查询的Spring数据存储库方法

@查询(value=“SELECT g.*,gm.*FROM group g LEFT JOIN group_members gm ON g.group_id=gm.group_id and gm.user_id=:userId WHERE g.group_id=:groupId”,nativeQuery=true)GroupDetails getGroupDeails(@Param(“userId”)整数用户Id,@Param(“groupId”)整型组Id);

我想把结果映射到非实体POJO组详细信息.

有可能吗,你能举个例子吗?

0

8个答案8

重置为默认值
179

我认为最简单的方法是使用所谓的投影。它可以将查询结果映射到接口。使用SqlResultSet映射是不协调的,并使您的代码难看:)。

一个来自spring数据JPA源代码的示例:

公共接口UserRepository扩展了JpaRepository<User,Integer>{@查询(value=“SELECT firstname,lastname FROM SD_User WHERE id=?1”,nativeQuery=true)NameOnly findByNativeQuery(整数id);公共静态接口NameOnly{字符串getFirstname();字符串getLastname();}}

您还可以使用此方法获取投影列表。

查看这个春季数据JPA文档条目,了解更多有关预测的信息。

注1:

记住用户定义为普通的实体-投影接口中的字段必须与此实体中的字段匹配。否则可能会破坏字段映射(获取名字()可能返回姓氏等的值)。

注2:

如果您使用SELECT表格.列。。。表示法总是定义与实体名称匹配的别名。例如,此代码无法正常工作(投影将为每个getter返回null):

@查询(value=“SELECT user.firstname,user.lastname FROM SD_user user WHERE id=?1”,nativeQuery=true)NameOnly findByNativeQuery(整数id);

但这很好:

@查询(value=“SELECT user.firstname AS firstname,user.lastname AS lastname FROM SD_user user WHERE id=?1”,nativeQuery=true)NameOnly findByNativeQuery(整数id);

如果是更复杂的查询,我宁愿使用使用而是使用自定义存储库。

12
  • 这是一种更清洁的解决方案。我已经检查过了,但性能比使用SqlResultSetMapping要差得多(大约慢30-40%:() 评论 2018年6月23日10:32
  • 效果很好!如果您想在其他地方使用,请将接口设置为公共接口
    – 胫骨
    评论 2018年6月27日12:24
  • 如果要提取XML类型(clob)字段,则不起作用。有什么建议吗?
    – 阿施施
    评论 2018年9月5日12:47
  • 1
    @SalmanKazmi我甚至不会考虑这样做,因为如果数据库中的表发生更改,那么视图对象也必须更改,所以维护这个投影将是一件很麻烦的事情。但是,如果投影中的字段与interface/dto中的字段相同,那么它应该可以工作。 评论 2018年12月3日8:40
  • 2
    使用小数据集是可以的。但我已经测试了100000个数据,在没有投影情况下,服务性能大约下降到1/12。换句话说,在没有投影的情况下,web服务的速度大约快12倍。
    – 白牙
    评论 2022年4月13日6:52
87

假设GroupDetails与orid的答案一样,您是否尝试过JPA 2.1@构造函数结果?

@SqlResultSet映射(name=“groupDetailsMapping”,类={@构造函数结果(targetClass=组详细信息.class,列={@ColumnResult(name=“GROUP_ID”),@ColumnResult(name=“USER_ID”)})})@NamedNativeQuery(name=“getGroupDetails”,query=“SELECT g.*,gm.*FROM group g LEFT JOIN group_members gm ON g.group_id=gm.group_id and gm.user_id=:userId WHERE g.group_id=:groupId”,resultSetMapping=“groupDetailsMapping”)

并使用以下存储库界面:

GroupDetails getGroupDeails(@Param(“userId”)整数用户Id,@Param(“groupId”)整型组Id);

根据Spring Data JPA文档,spring将首先尝试查找与方法名匹配的命名查询-因此,使用@名称NativeQuery,@SqlResultSet映射@构造函数结果你应该能够做到那种行为

8
  • 20
    为了使spring数据能够匹配NamedNativeQuery,需要将域实体的类名后跟一个点作为NamedNativeQuery名称的前缀。因此名称应该是(假设域实体是Group)“Group.getGroupDetails”。 评论 2016年11月22日8:08
  • 如何返回此类对象的列表? 评论 2017年7月17日12:35
  • 1
    要让它工作,应该组详细信息标有@实体? 如果可能的话,你能告诉我注释在哪一类上吗@名称NativeQuery必须应用?
    – 马努
    评论 2017年10月15日17:32
  • @SqlResultSet映射@名称NativeQuery注释必须出现在Spring数据存储库中使用的实体上(例如公共接口CustomRepository扩展了CrudRepository<CustomEntity,Long>它是客户实体类别) 评论 2017年11月29日14:05
  • 1
    不要忘记将@Query(nativeQuery=true)放在GroupDetails getGroupDeails(@Param(“userId”)上。。。 评论 2019年4月10日16:11
24

我认为Michal的方法更好。但是,还有一种方法可以从本机查询中获取结果。

@查询(value=“SELECT g.*,gm.*FROM group g LEFT JOIN group_members gm ON g.group_id=gm.group_id and gm.user_id=:userId WHERE g.group_id=:groupId”,nativeQuery=true)字符串[][]getGroupDetails(@Param(“userId”)整数用户Id,@Param;

现在,您可以将这个2D字符串数组转换为所需的实体。

4
  • 简约典雅
    – 厕所
    评论 2020年8月29日21:33
  • 如何从2D数组转换为类? 评论 2020年11月23日19:07
  • 9
    这太冒险了。若表中的列顺序发生了更改(例如,添加了新列,或修改了现有列),则可以混合结果值。例如,如果您的实体客户端ID字段,但列已从客户端id用户id然后,由于缺少列名验证,很难检测到这种更改。 评论 2021年3月4日11:29
  • @manushin-igor建议的简单解决方法是在查询中按顺序登记列名,以便安全地映射它。类似如下:@Query(value=“SELECT g.group_id,g.userId,gm.group_name FROM group g LEFT JOIN group_members gm ON g.group_id=gm.group2d and gm.user_id=:userId WHERE g.group_id=:groupId”,nativeQuery=true)String[][]getGroupDetails(@Param(“userId”)Integer userId,@Param; 评论 2023年11月6日9:36
15

您可以按照自己的方式编写本机或非本机查询,还可以使用自定义结果类的实例包装JPQL查询结果。使用查询中返回的相同列名称创建DTO,并使用查询返回的相同序列和名称创建全参数构造函数。然后使用以下方法查询数据库。

@查询(“SELECT NEW example.CountryAndCapital(c.name,c.capital.name)FROM Country AS c”)

创建DTO:

包装示例;公共阶级国家和首都{public String countryName;public String capitalName;public CountryAndCapital(字符串countryName,字符串capitalName){this.countryName=国家名称;this.capitalName=资本名称;}}
5
  • 更正:相同的名称不是强制性的。。。构造函数和返回结果集中的参数序列完全相同。 评论 2019年4月18日20:38
  • 只有当Country是您的java实体类时,这才有效。如果Country不是您的java实体类,则不会这样做。 评论 2019年5月10日2:14
  • 14
    你说这也适用于本机查询吗?你能举个例子吗? 评论 2020年4月9日9:29
  • 6
    OP请求本机查询,但给出的示例是非本机查询 评论 2020年9月22日7:08
  • “您可以按照自己的方式编写本机或非本机查询,还可以使用自定义结果类的实例包装JPQL查询结果。”-本机SQL查询无法做到这一点。
    – 维萨尔
    评论 2022年6月8日19:43
2

使用JPA投影在您的情况下,可能需要将数据检索为自定义类型的对象。这些类型反映了根类的部分视图,只包含我们关心的属性。这就是预测有用的地方。首先将Entity声明为@immutable

@实体@不可变的

公众类地址{

@身份证私有Long id;

设置存储库

公共接口AddressView{字符串getZipCode();}

然后在存储库界面中使用它:

公共接口AddressRepository扩展存储库{@查询(“EXEC SP_GETCODE?1”)列表<AddressView>getAddressByState(字符串状态);}
2

这是我转换为Map然后转换为自定义Object的解决方案

私有ObjectMapper对象映射器;公共静态列表<Map<String,Object>>convertTuplesToMap(列表<?>元组){列表<Map<String,Object>>result=new ArrayList();tuples.forEach(对象->{if(Tuple single的对象实例){Map<String,Object>tempMap=new HashMap<>();for(TupleElement<?>键:single.getElements()){tempMap.put(key.getAlias(),single.get(key));}result.add(tempMap);}其他{throw new RuntimeException(“Query should return instance of Tuple”);}});返回结果;}public<T>List<T>parseResult(List<?>List,Class<T>clz){列表结果=新数组列表();convertTuplesToMap(列表).forEach(map->{result.add(objectMapper.convertValue(map,clz));});返回结果;}公共静态类CustomDTO{私有字符串param1;private Integer参数2;私有偏移日期时间参数3;}公共列表<CustomDTO>doSomeQuery(){查询查询=entityManager.createNativeQuery(“SELECT param1,param2 param3…”,Tuple.class);return parseResult(query.getResultList(),CustomDTO.class);}
0

使用界面中的默认方法,获取EntityManager以获得设置ResultTransformer的机会,然后可以返回纯POJO,如下所示:

final String sql=“SELECT g.*,gm.*FROM group g LEFT JOIN group_members gm ON g.group_id=gm.group_id and gm.user_id=?WHERE g.group_id=?”;默认组详细信息getGroupDetails(整数userId,整数groupId){return BaseRepository.getInstance().uniqueResult(sql,GroupDetails.class,userId,groupId);}

BaseRepository.java是这样的:

@持久性上下文公共实体管理器em;public<T>T uniqueResult(字符串sql,类<T>dto,对象…参数){Session Session=em.unwrap(Session.class);NativeQuery q=会话.createSQLQuery(sql);if(params!=空){for(int i=0,len=params.length;i<len;i++){对象参数=参数[i];q.setParameter(i+1,参数);}}q.setResultTransformer(Transformers.aliasToBean(dto));return(T)q.uniqueResult();}

此解决方案不会影响存储库界面文件中的任何其他方法。

0

如果您正在寻找使用@repository和@service结构在springboot中运行自定义SQL查询。请看一看。

https://stackoverflow.com/a/71501509/4735043

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.