springDataJpa入门教程(7)-基于springDataJpa投影(Projection)返回自定义实体类对象

springDataJpa入门教程

springDataJpa入门教程(7)-基于springDataJpa投影(Projection)返回自定义实体类对象

在使用springDataJpa做查询操作时,如果查询的结果集和实体类不对应,通常的做法有三种:

  1. 返回Map对象,然后再手动将Map对象里的值取出来,一个一个set到自定义对象里面。
  2. 返回Object数组,然后按照数组下标,将值一个一个set到自定义对象里面。
  3. 在自定义对象类中,添加构造方法,然后通过类似于"@Query(value = “select new 包路径.类名(args1,args2…) from tb_user”)"的方式,将查询结果封装到自定义实体类对象中。

首先说第三种方法,这种方法要创建构造方法,通常情况下构造方法的参数会比较多,构造方法显示很臃肿,而且构造方法的参数顺序要和查询结果集的参数顺序完全对应,否则就会报错,这样的话参数一多很容易出错。再有,如果自定义实体类在多个查询里面使用,结果就是构造方法泛滥,难以维护。
第二种方法,Object数组方式,需要强转才能set到对象里面,有的情况还要做null值判断,不是很方便。
第一种方法,个人认为比第二种和第三种要理想一些,但是还是要自己手动从Map对象里面get值,然后再set,也避免不了要进行类型强转。

今天来给大家介绍另一种方法,springDataJpa的投影Projection,可以很容易实现将结果集封装到自定义对象里面,并且代码也比较简洁。由于本人水平有限,教程中难免出现错误,敬请谅解,欢迎大家批评指正。源码地址:源码下载地址。java学习交流群:184998348,欢迎大家一起交流学习。下面来举个例子。

下面是需要用到的实体类

package com.thizgroup.jpa.study.model;

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "tb_user")
@Data//使用lombok生成getter、setter
@NoArgsConstructor//生成无参构造方法
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(name = "name",columnDefinition = "varchar(64)")
  private String name;

  @Column(name = "mobile",columnDefinition = "varchar(64)")
  private String mobile;

  @Column(name = "email",columnDefinition = "varchar(64)")
  private String email;

  @Column(name = "age",columnDefinition = "smallint(64)")
  private Integer age;

  @Column(name = "birthday",columnDefinition = "timestamp")
  private Date birthday;

  //地址
  @Column(name = "address_id",columnDefinition = "bigint(20)")
  private Long addressId;

  @Column(name = "create_date",columnDefinition = "timestamp")
  private Date createDate;

  @Column(name = "modify_date",columnDefinition = "timestamp")
  private Date modifyDate;

  @Builder(toBuilder = true)
  public User(Long id,String name, String mobile, String email, Integer age, Date birthday,
      Long addressId) {
    this.id = id;
    this.name = name;
    this.mobile = mobile;
    this.email = email;
    this.age = age;
    this.birthday = birthday;
    this.addressId = addressId;
  }
}

package com.thizgroup.jpa.study.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;

@Entity
@Table(name="tb_address")
@Data//使用lombok生成getter、setter
public class Address {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(name = "country",columnDefinition = "varchar(64)")
  private String country;

  @Column(name = "province",columnDefinition = "varchar(64)")
  private String province;

  @Column(name = "city",columnDefinition = "varchar(64)")
  private String city;

}
需求: 根据用户id查询用户信息,用户信息包括姓名、年龄、手机号、国家、城市、地址(地址=国家+城市)。

下面我们来自定义一个接口用于封装查询的结果集,注意:这里定义的是interface,而不是Class,不需要给接口定义实现类

package com.thizgroup.jpa.study.dto;

import org.springframework.beans.factory.annotation.Value;

/**
 * 用户信息 jpa 投影
 */
public interface UserProjection {

  public Long getId();

  public String getName();//姓名

  public Integer getAge();//年龄

  public String getMobile();//手机号

  public String getCountry();//国家

  public String getCity();//城市

  @Value("#{target.country+target.city}")
  public String getAddress();//地址

}

address这个字段比较特殊,在结果集中并没有一个名为address的字段,这个address是由country字段和city字段聚合得到的。
IUserService接口中添加一个查询方法,

  /**
   * 使用jpa投影查询用户信息
   * @param id
   * @return
   */
  UserProjection findUserInfoById(Long id);

然后在UserServiceImpl实现类中实现该方法,

package com.thizgroup.jpa.study.service.impl;

import com.thizgroup.jpa.study.dao.UserRepository;
import com.thizgroup.jpa.study.dto.UserProjection;
import com.thizgroup.jpa.study.model.User;
import com.thizgroup.jpa.study.service.IUserService;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

@Service
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public class UserServiceImpl implements IUserService {

  @Autowired
  private UserRepository userRepository;
	
   @Override
  public UserProjection findUserInfoById(Long id) {
    return userRepository.findUserInfoById(id);
  }
  
}

接下来在UserRepository中添加findUserInfoById查询方法,

package com.thizgroup.jpa.study.dao;

import com.thizgroup.jpa.study.dto.UserProjection;
import com.thizgroup.jpa.study.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface UserRepository extends JpaRepository<User,Long>, JpaSpecificationExecutor<User> {

  @Query("select u.id as id,u.name as name,u.age as age,u.mobile as mobile,a.country as country,"
      + " a.city as city from User u "
      + " left join Address a on u.addressId=a.id where u.id =:id")
  UserProjection findUserInfoById(@Param("id") Long id);
}

注意:写sql语句一定要给字段起别名,并且别名必须和自定义接口中的属性名保持一致。
完成以上几步,就能直接拿到一个UserProjection 对象,整个代码看上去十分简洁。
下面写个单元测试来验证一下,

package com.thizgroup.jpa.study.service;

import com.thizgroup.jpa.study.JpaApplication;
import com.thizgroup.jpa.study.dto.UserProjection;
import com.thizgroup.jpa.study.model.User;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@SpringBootTest(classes={JpaApplication.class})
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional(readOnly = false,propagation = Propagation.REQUIRED)
public class UserServiceImplTest {

  @Autowired
  private IUserService userService;

    @Test
  public void findUserInfoByIdTest(){
    UserProjection userProjection = userService.findUserInfoById(1L);
    System.out.println("id:"+userProjection.getId()+",age:"
    +userProjection.getAge()+",country:"+userProjection.getCountry()
    +",address:"+userProjection.getAddress());
  }
}

运行一下单元测试,结果如下:

id:1,age:35,country:中国,address:中国上海

至此,springDataJpa使用Projection返回自定义实体类对象就介绍完了,有需要源码的朋友,请到git上下载源码,源码地址:源码下载地址。java学习交流群:184998348,欢迎大家一起交流学习。

上一篇:springDataJpa入门教程(6)-多表动态条件查询+分页
下一篇:springDataJpa入门教程(8)-JPA EnableJpaAuditing 审计功能
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Data JPA 中,我们可以通过定义 Repository 接口中的方法,使用方法名来自动生成查询语句。但是有时候,我们需要自定义查询结果,比如说只需要查询结果中的部分字段,或者对查询结果进行聚合操作等。这时,我们可以使用 Spring Data JPA 提供的投影Projection)功能来自定义查询结果。 投影是指将实体类中的一部分属性或关联属性映射成一个接口或类,从而返回一个自定义的结果集。Spring Data JPA 支持三种投影方式: 1. 接口投影:定义一个接口,接口中声明需要的属性,Spring Data JPA 将根据接口定义的属性生成查询结果。 2. 类投影:定义一个类,类中声明需要的属性,Spring Data JPA 将根据类定义的属性生成查询结果。 3. 动态投影:可以根据查询条件动态地返回不同的投影结果,比如说根据用户的角色返回不同的查询结果。 下面是一个简单的例子,演示如何使用接口投影自定义查询结果: 定义一个接口投影: ```java public interface UserProjection { String getUsername(); String getEmail(); } ``` 在 UserRepository 中使用接口投影: ```java public interface UserRepository extends JpaRepository<User, Long> { List<UserProjection> findByUsername(String username); } ``` 通过上面的代码,我们可以在 UserRepository 中定义一个方法 findByUsername,该方法会返回一个 List<UserProjection> 类型的结果集,该结果集中只包含 username 和 email 两个字段。 当我们调用 findByUsername 方法时,Spring Data JPA 将根据方法名自动生成查询语句,并根据 UserProjection 接口中定义的属性生成查询结果。 当然,除了接口投影,还有其他的投影方式,你可以根据自己的需求选择适合的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值