此文章只讨论两种方式查询, 使用jpa关键字查询和自定义sql
// 方式1
1. List<UserName> findByName(String name);
// 方式2
2. @Query(value = "select name from t_users where name = ?", nativeQuery = true)
List<UserName> findByName2(String name);
使用接口接收
即通过定义一个接口接口 UserName,两种方式都支持通过定义接口接受返回,JPA原生支持
public interface UserName {
String getNname();
}
自定义对象接收
方式一 JAP原生支持自定义对象,但条件是而且只有一个构造函数,有些工具类需要用到默认构造函数,不方便
方式二 JAP不支持自定义对象,会返回Object[] 对象数组
解决方案
例子只是解决方式二, 如果需要解决方式一构造函数问题,可以借鉴,下面例子自己实现 我们定义一个接口ReabamDTO
public interface ReabamDTO {
}
GenericConverter实现类
内容是map 转 ReabamDTO
public final class JpaConvert implements GenericConverter {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Map.class, ReabamDTO.class));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return map2Pojo(source, targetType.getObjectType());
}
public static <T> T map2Pojo(Object entity, Class<T> tClass) {
CopyOptions copyOptions = CopyOptions.create()
.setIgnoreCase(true)
.setIgnoreError(true)
.setIgnoreNullValue(true)
.setFieldNameEditor(StrUtil::toUnderlineCase);
return BeanUtil.toBean(entity, tClass, copyOptions);
}
}
将自定义convert加入到GenericConversionService
@Configuration
public class JpaConfig {
@PostConstruct
public void init() {
GenericConversionService genericConversionService = ((GenericConversionService) DefaultConversionService.getSharedInstance());
genericConversionService.addConverter(new JpaConvert());
}
}
测试
public interface UsersRepository extends JpaRepository<Users, Integer> {
List<UserName> findByName(String name);
@Query(value = "select name from t_users where name = ?", nativeQuery = true)
List<UserName> findByName2(String name);
}
@Data
@ToString
@NoArgsConstructor
public class UserName implements ReabamDTO {
private String name;
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = DemoApplication.class, properties = {"spring.profiles.active=local"})
class DemoApplicationTests {
@Autowired
private UsersRepository usersRepository;
@Test
void contextLoads() {
Users users = new Users();
users.setName("A");
users.setAge(1);
users.setAddress("AA");
usersRepository.save(users);
}
/**
* 非自定义sql可以返回自定义对象
*/
@Test
void t2() {
List<UserName> a = usersRepository.findByName("A");
System.out.println(a);
}
/**
* 自定义sql 自定义对象
*/
@Test
void t3() {
List<UserName> a = usersRepository.findByName2("A");
System.out.println(a);
}
}
原理可选择观看
查看报错信息,找不到转换器异常, 那么只需要加入对应的装换器就可以了,
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.architeture.demo.UserName]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175)
at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.convert(ResultProcessor.java:309)
at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.lambda$and$0(ResultProcessor.java:225)
at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:236)
at org.springframework.data.repository.query.ResultProcessor.processResult(ResultProcessor.java:156)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:158)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:143)
通过查看源码, 获取转换器的位置 org.springframework.core.convert.support.GenericConversionService#getConverter
@Nullable
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
converter = this.converters.find(sourceType, targetType);
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
this.converterCache.put(key, NO_MATCH);
return null;
}
继续看源码, 查找能加入转换器位置 converter = this.converters.find(sourceType, targetType);
@Nullable
public GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {
// Search the full type hierarchy
// 获取
List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
if (converter != null) {
return converter;
}
}
}
return null;
}
此方法 源对象TupBackedMap父类或接口和UserName父类或接口进行组合,获取到对应的转换器
TupBackedMap的父类或接口
org.springframework.core.convert.support.GenericConversionService#addConverter(org.springframework.core.convert.converter.GenericConverter)
@Nullable
private GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters
ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
if (convertersForPair != null) {
GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
if (converter != null) {
return converter;
}
}
// Check ConditionalConverters for a dynamic match
for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
return globalConverter;
}
}
return null;
}
可以看到在 ConvertersForPair convertersForPair = this.converters.get(convertiblePair); 在convertes中加入转换器
org.springframework.core.convert.support.GenericConversionService.Converters
private static class Converters {
private final Map<ConvertiblePair, ConvertersForPair> converters = new LinkedHashMap<>(36);
public void add(GenericConverter converter) {
}
}
查看调用 add 方法位置, org.springframework.core.convert.support.GenericConversionService#addConverter(org.springframework.core.convert.converter.GenericConverter) 最终定位到这里
原文:Spring Data Jpa 原生SQL返回自定义对象最简洁方式 - evan888的个人空间 - OSCHINA - 中文开源技术交流社区
作者:evan888