spring data jpa 查询自定义字段,转换为自定义实体方式

目标:查询数据库中的字段,然后转换成 JSON 格式的数据,返回前台。

环境:idea 2016.3.4, jdk 1.8, mysql 5.6, spring-boot 1.5.2

背景:首先建立 entity 映射数据库(非专业 java 不知道这怎么说)

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String userName;    // 账号
    private String password;    // 密码
    // getter setter 方法略过
}

然后建立与之对应的 model

public class UserModel implements Serializable {
    // 一些属性
}

这里我们分情况讨论

首先第一种情况:

查询的字段与表中的字段全部对应(就是查表里所有的字段,但是使用 Model 作为接收对象)

这种情况比较简单,调用 Repository 提供的方法,返回一个 entity , 然后将 entity 的属性复制到 model 中。像这样

UserModel user = new UserModel();
User userEntity = new User();
// 一个工具类,具体使用方法请百度
BeanUtils.copyProperties(user, userEntity);

第二种情况:只查询指定的几个字段

现在我有张表,有字段如下:

@Entity
@Table(name = "user_info")
public class UserInfo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name = "用户";     // 昵称
    private String signature;         // 个性签名
    private String gender = "未知";   // 性别
    private String description;       // 个人说明
    private String avatar;            // 头像
    private Long role;                // 权限
    private Boolean disable;          // 是否冻结
    private Date createTime;          // 创建时间
    private Boolean isDelete;         // 是否删除
    private Long userId;              // 用户 Id
    // ...
}

但是我只需要查询指定的几个字段,然后转换成 JSON,返回给前台,咋办呢?

第一种方法:使用 model 查询时转化

首先建立一个 model ,写上自己想要查询的字段,然后写上构造函数,这步很重要,因为spring jpa 转化时会调用这个构造方法

public class MyModel implements Serializable {
    private String userName;
    private String name;
    private String gender;
    private String description;
    public MyModel() {};
    public MyModel(String userName, String name, String gender, String description) {
        this.userName = userName;
        this.name = name;
        this.gender = gender;
        this.description = description;
    }
}

然后在 dao 类中写查询方法

@Query(value = "select new pers.zhuch.model.MyModel(u.userName, ui.name, ui.gender, ui.description) from UserInfo ui, User u where u.id = ui.userId")
public List<MyModel> getAllRecord();

直接在查询语句中 new model 框架底层会调用它,然后返回这个对象(这里我写了完整的类路径,不写的时候它报错说找不到类型什么的)

然后就可以获得只有指定字段的 model 了。然后就把它转成 JSON 格式就 O 了。

第二种方法:在service 里边转换成 JSON

原理其实和第一种方法差不多,只是处理结果的方式不太一样,只是这种方法我们就不在 hql 中 new Model 了,直接写查询方法

@Query(value = "select new map(u.userName, ui.name, ui.gender, ui.description) from UserInfo ui, User u where u.id = ui.userId")
public List<Map<String, Object>> getCustomField();

直接new map(这里得是小写,不知道大写有木有问题,反正没试,编译器提示是要小写的)

然后返回的结果是这样的

[
    {
        "0": "admin",
        "1": "你猜",
        "2": "男",
        "3": "一段描述"
    }, {
        "0": "abc",
        "1": "你猜人家",
        "2": "女",
        "3": "没事先挂了"
    }
]

然后在 service 层里直接封装成 JSON 对象,返回

List<JsonObject> list = new ArrayList();
for(Map map : result) {
    JsonObject j = new JsonObject();
    j.addProperty(attrName, val);
    ...
    list.add(j);
}
gson.toJson(list);

还有一种返回结果,这样写:

@Query(value = "select u.userName, ui.name, ui.gender, ui.description from UserInfo ui, User u where u.id = ui.userId")
public List<Object> getCustomField();

返回结果是这样的格式:

[
    [
        "admin",
        "你猜",
        "男",
        "一段描述"
    ], [
        "abc",
        "你猜人家",
        "女",
        "没事先挂了"
    ]
]

返回的是数组,也一样可以通过上面的方法转成 json ,这里我的程序中出现了一点点 BUG,就是空值的字段不会在数组中,不知道为什么。

这种方法必须明确的知道查询了哪些字段,灵活性比较差,虽然它解决了手头的问题。还有就是版本的不同,有可能会出现丢失空字段的情况,我个人特别的不喜欢这样的方法,万一我实体几十个字段,写着写着忘了写到哪了,就 over 了

第三种方法:返回一个便于转换成 json 格式的 list

其实和上面很相似,都是 dao 层返回一个 List < Map < String, Object >>,但是上面的结果集返回的 Map 的 key 只是列的下标,这种方式稍微理想一点点,就是 Map 的 key 就是查询的列名。

但是这种方式需要实现自定义 Repository( 这里不详细介绍,请自行百度 ),并且只是 jpa 集成 hibenate 的时候可以使用。

public List getCustomEntity() {
    String sql = "select t.id, t.name, t.gender, t.is_delete, t.create_time, t.description from t_entity t";
    Query query = em.createNativeQuery(sql);
    // Query 接口是 spring-data-jpa 的接口,而 SQLQuery 接口是 hibenate 的接口,这里的做法就是先转成 hibenate 的查询接口对象,然后设置结果转换器
    query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
    return query.getResultList();
}

这种方法返回的就是比较标准的 JSON 格式的 java 对象了,只需要用 jackson 或者 Gson 转一下就是标准的 json 了

[
    {
        attr: val,
        ...
    },
    {
        attr: val,
        ...
    },
]

这种方式其实已经比较理想了,因为直接就能返回到前台,但是有时候,结果不是一条 sql 能够解决的,得两条或者以上的 sql 来解决一个复杂的查询需求,这个过程中,结果比较需要转换成 pojo,以便于组装操作。

第四种方案:dao 中直接转成 pojo 返回

这个方案还是依赖于 hibenate,有点操蛋,但是更明确一些。

public List getCustomEntity() {
    String sql = "select t.id, t.name, t.gender, t.is_delete as isEnable, t.create_time as createTime, t.description from t_entity t";
    Query query = em.createNativeQuery(sql);
    query.unwrap(SQLQuery.class)
    // 这里是设置字段的数据类型,有几点注意,首先这里的字段名要和目标实体的字段名相同,然后 sql 语句中的名称(别名)得与实体的相同
            .addScalar("id", StandardBasicTypes.LONG)
            .addScalar("name", StandardBasicTypes.STRING)
            .addScalar("gender", StandardBasicTypes.STRING)
            .addScalar("isEnable", StandardBasicTypes.BOOLEAN)
            .addScalar("createTime", StandardBasicTypes.STRING)
            .addScalar("description", StandardBasicTypes.STRING)
            .setResultTransformer(Transformers.aliasToBean(EntityModel.class));
    return query.getResultList();
}

这次返回的就是 List 了。这里要注意的是 StandardBasicTypes这个常量类,在一些旧版本中,是 Hibenate 类,具体哪个包我不知道,我这个版本中是换成了前面的那个常量类

继承jpa Repository 写自定义方法查询

今天在写jpa查询的时候,遇到了添加自定义方法,项目启动报错原因,现总结如下:

首先定义实体类

@Entity
@Table(name = "user")
Class User{
     @Id
    @GeneratedValue
      int id;
      @Column
      String age;
      @Column
      String school;
      @Column
      String userName;
  set,get方法 (省略)
}
public interface UserRepository extends JpaRepository<User, Long> {
      List<User> findByUsernameLike(String username);
     List<User> aaa();
}

启动项目时,项目报错提示信息为:

org.springframework.data.mapping.PropertyReferenceException: No property aaa found for type com.fpi.safety.common.entity.po.User

再将List<User> aaa();方法去掉后,项目又可以正常启动运行

是什么原因呢?

经查找,原来是继承jpa,必须满足一些规则,规则如下

Spring Data JPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如find,findBy,read,readBy,get,getBy,然后对剩下的部分进行解析。

假如创建如下的查询:findByUserName(),框架在解析该方法时,首先剔除findBy,然后对剩下的属性进行解析,假设查询实体为User

1:先判断userName(根据POJO规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;

2:从右往左截取第一个大写字母开头的字符串此处是Name),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设用户为查询实体的一个属性;

3:接着处理剩下部分(UserName),先判断用户所对应的类型是否有userName属性,如果有,则表示该方法最终是根据“User.userName”的取值进行查询;否则继续按照步骤2的规则从右往左截取,最终表示根据“User.userName”的值进行查询。

4:可能会存在一种特殊情况,比如User包含一个的属性,也有一个userNameChange属性,此时会存在混合。可以明确在属性之间加上“_”以显式表达意思,比如“findByUser_NameChange )“或者”findByUserName_Change()“

从上面,我们可以得知,jap在解析是,aaa在user类中是没有属性的,所以报错No property aaa found.

如果我们想要使用jap框架,又不想再多增加一个自定义类,则必须符合其命名规则

如果,你记不住jpa的规则也没关系,你可以自己再多写一个类来实现自定义查询方法

如下:

1. 自定义一个接口,该接口用来声明自己额外定义的查询。

public interface UseerRepositoryTwo {
    public List<User> searchUser(String name, int id);
}

2. 创建一个接口,该接口 extends JpaRepository 或者 CurdRepository, 以及上面自己定义的接口 UseerRepositoryTwo

public interface UserRepositoryTwoService extends CrudRepository<LogDTO, Integer>, CustomizedLogRepository {
}

3. 实现UserRepositoryTwoService

注意此处的类名,必须以 2 中创建的接口的名字UserRepositoryTwoService,后面加上 Impl 来声明,而不是写成 UseerRepositoryTwoImpl

public class UserRepositoryTwoServiceImpl implements UserRepositoryTwoService {
    @Autowired
    @PersistenceContext
    private EntityManager entityManager;
    @Override
    public List<User> searchLogs(int Id, String name) {
        ......
    }
}

自己在写自定义实现即可

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Data JPA实现动态查询的两种方法

    前言 一般在写业务接口的过程中,很有可能需要实现可以动态组合各种查询条件的接口.如果我们根据一种查询条件组合一个方法的做法来写,那么将会有大量方法存在,繁琐,维护起来相当困难.想要实现动态查询,其实就是要实现拼接SQL语句.无论实现如何复杂,基本都是包括select的字段,from或者join的表,where或者having的条件.在Spring Data JPA有两种方法可以实现查询条件的动态查询,两种方法都用到了Criteria API. Criteria API 这套API可用于构建对数据

  • Spring Data Jpa的四种查询方式详解

    这篇文章主要介绍了Spring Data Jpa的四种查询方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.调用接口的方式 1.基本介绍 通过调用接口里的方法查询,需要我们自定义的接口继承Spring Data Jpa规定的接口 public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> 使用这

  • Spring Data Jpa实现自定义repository转DTO

    近期项目中需要 关联 几张表再把字段转出来,在这里记录以下,我感觉网上写的都不太规范和清晰. @Entity @SqlResultSetMapping( name="TestMapping", entities = { @EntityResult( entityClass = com.xxx.xx.data.model.TestEntity.class, fields = { @FieldResult(name="id",column="id")

  • Spring Boot下如何自定义Repository中的DAO方法

     环境配置介绍 jdk 1.8, spring Boot 1.5.3.RELEASE, MySQL, Spring Data, JPA 问题描述 Spring Data提供了一套简单易用的DAO层抽象与封装,覆盖的CURD的基本功能,但是在诸多的情况下,需要用户自定义DAO的实现方法,来实现更为复杂和精细的数据库访问操作,该如何来解决这个问题? 目标描述 这里我们以自定义testAA的方法为例,来介绍如何实现自定义的DAO方法扩展. 数据库表的定义 我们这里定义了一个非常简单的mycity表,来

  • spring data jpa查询一个实体类的部分属性方式

    目录 springdatajpa查询一个实体类的部分属性 首先我们定义两个实体类 然后创建person实体类的repository 返回结果只包含firstName和lastName两个属性 springdatajpa查询部分字段.多余附加字段 第一种方法:使用model查询时转化 第二种方法:在service里边转换成JSON 第三种方法:select语句部分字段使用默认值 spring data jpa查询一个实体类的部分属性 使用Spring Data Repository查询时候,通常情

  • Spring Data JPA框架快速入门之自定义Repository接口

    目录 自定义Repository接口 repository接口定义 使用Repository接口 自定义Repository接口 要定义一个repository接口,你首先需要自定义一个实体类专用的Repository接口.该接口必须扩展 Repository,并将其类型指定为实体类和实体类的 ID 类型. 如果你想为该实体类资源类型开放CRUD方法,请直接继承CrudRepository而不是Repository. repository接口定义 通常,你的repository接口会扩展Repo

  • 使用Spring Data Jpa查询全部并排序

    目录 Spring Data Jpa查询全部并排序 1.Repository层只需要简单地extends JpaRepository 2.Service层中如下 JPA之排序问题 1.总览 2.使用JPA / JQL API排序 3.使用JPA条件查询对象API进行排序 Spring Data Jpa查询全部并排序 1.Repository层只需要简单地extends JpaRepository 依旧不用写任何东西.因为它自己其实有一个findAll(Sort sort)的方法,直接用就可以了.

  • Spring Data JPA查询方式及方法名查询规则介绍

    目录 Spring Data JPA查询方式及方法名查询规则 一.通过解析方法名创建查询 二.使用 @Query 创建查询 JPA 常用查询方法记录 CrudRepository 默认带的查询方法 简单的扩展-以字段为关键字进行查询 使用@Query 进行复杂查询 使用 Specification 进行复杂查询 Predicate CriteriaBuilder Root Spring Data JPA查询方式及方法名查询规则 Spring Data JPA 一.通过解析方法名创建查询 在执行查

  • spring data jpa 查询自定义字段,转换为自定义实体方式

    目标:查询数据库中的字段,然后转换成 JSON 格式的数据,返回前台. 环境:idea 2016.3.4, jdk 1.8, mysql 5.6, spring-boot 1.5.2 背景:首先建立 entity 映射数据库(非专业 java 不知道这怎么说) @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long

  • Spring Data JPA实现查询结果返回map或自定义的实体类

    目录 Spring Data JPA查询结果返回map或自定义的实体类 1.工具类 2.具体应用 spingboot:jpa:Spring data jpa 返回map 结果集 Spring Data JPA查询结果返回map或自定义的实体类 在JPA中我们可以使用entityManager.createNativeQuery()来执行原生的SQL语句,并且JPA的底层实现都是支持返回Map对象的. 例如: EclipseLink 的 query.setHint(QueryHints.RESUL

  • Spring Data JPA实现查询结果返回map或自定义的实体类

    目录 SpringDataJPA查询结果返回map或自定义的实体类 1.工具类 2.具体应用 spingboot:jpa:Springdatajpa返回map结果集 Spring Data JPA查询结果返回map或自定义的实体类 在JPA中我们可以使用entityManager.createNativeQuery()来执行原生的SQL语句,并且JPA的底层实现都是支持返回Map对象的. 例如: EclipseLink 的 query.setHint(QueryHints.RESULT_TYPE

  • spring data jpa使用详解(推荐)

    使用Spring data JPA开发已经有一段时间了,这期间学习了一些东西,也遇到了一些问题,在这里和大家分享一下. 前言: Spring data简介: Spring Data是一个用于简化数据库访问,并支持云服务的开源框架.其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务. Spring Data 包含多个子项目: Commons - 提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化 JPA - 简化创建 JPA 数据访问层和跨存储的持久层

  • Spring Data Jpa 复杂查询方式总结(多表关联及自定义分页)

    目录 SpringDataJpa复杂查询总结 1.查找出Id小于3,并且名称带有shanghai的记录 2.通过旅店名称分页查询旅店以及城市的所有信息 3.HQL通过旅店名称查询旅店以及城市的所有信息 4.HQL通过旅店名称查询旅店以及城市的所有信息直接返回实体类 5.动态查询旅店以及城市的所有信息直接返回实体类 JPA#复杂查询#自定义查询 编写自定义SQL基于下面信息 基于最前面的信息,要编写自定义SQL Spring Data Jpa复杂查询总结 只是做一个总结所以就不多说废话了 实体类

  • Spring Data Jpa多表查询返回自定义实体方式

    目录 SpringDataJpa多表查询返回自定义实体 Repository 好下面到单元测试 自定义实体 SpringDataJpa多表查询返回自定义VO的问题 下面是我的代码 下面是我的dao层,重点 SpringDataJpa多表查询返回自定义实体 比如来看一下这样的一条SQL语句,这是一个三张表的多表查询,显然在JPA中用一个实体类是接受不了这些参数的 select  t1.id as chapterId , t1.name as chapterName , t2.id as unitI

随机推荐