我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JPA如何使用nativequery多表关联查询返回自定义实体类

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JPA如何使用nativequery多表关联查询返回自定义实体类

JPA nativequery多表关联查询返回自定义实体类

JPA官方推荐的多表关联查询使用不便,接触的有些项目可能会使用JPA 做简单查询,Mybaits做复杂查询。所以想要寻找一种好用的解决方案。

JPA多表关联的实现方式

1.使用Specification实现映射关系匹配,如@ManyToOne等

2.使用NativeQuery等sql或hql来实现

优缺点对比

1.映射关系是hibernate的入门基础,很多人都会习惯去使用。个人不太喜欢这种方式,复用性太弱,且不灵活特别是在多表复杂业务情况下。

2.使用Specification方式需要继承JpaSpecificationExecutor接口,构造对应的方法后传入封装查询条件的Specification对象。逻辑上简单易懂,但是构造Specification对象需要拼接格式条件非常繁琐。

3.直接使用NativeQuery等方式实现复杂查询个人比较喜欢,直观且便利,弊端在于无法返回自定义实体类。需要手动封装工具类来实现Object到目标对象的反射。

使用sql并返回自定义实体类

个人比较喜欢的实现方式,不多说看代码


import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
 
@Repository
public class EntityManagerDAO {
    @PersistenceContext
    private EntityManager entityManager;
     
    @Transactional
    public List<BackstageUserListDTO> listUser(){
        String sql = "select a.create_time createTime," +
                "a.mobilephone phoneNum," +
                "a.email email,a.uid uid," +
                "a.enabled enabled," +
                "c.id_number idNumber," +
                " (case b.`status` when 1 then 1 else 0 end) status " +
                "from tbl_sys_user a " +
                "LEFT JOIN user_high_qic b on a.uid=b.u_id" +
                "LEFT JOIN user_qic c on a.uid=c.uid " +
                "ORDER BY status desc";
 
        SQLQuery sqlQuery = entityManager.createNativeQuery(sql).unwrap(SQLQuery.class);
        Query query = 
     sqlQuery.setResultTransformer(Transformers.aliasToBean(BackstageUserListDTO.class));
        List<BackstageUserListDTO> list = query.list();
        entityManager.clear();
        return list;
    }
}

public class BackstageUserListDTO implements Serializable{
    private static final long serid = 1L;
    private String createTime;
    private String phoneNum;
    private String email;
    private BigInteger uid;
    private Integer enabled;
    private String idNumber;
    private BigInteger status;
    //GETTER SETTER
}

这样一个需求如果使用前两种方式实现,无疑会非常麻烦。使用这种方式能够直接反射需要的自定义实体类。

可以根据需求整理封装成不同的方法,加入排序,分页等。我在这里主要提供一种方便的解决思路。

JPA多表关联动态查询(自定义sql语句)

项目需求,查询需求数据需要多表链接——>根据多种条件筛选查询到的数据,在网上查了很多资料最终选择这个字符串拼接查询

类似如此动态查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cA0h3Qnw-1590984326742)(../AppData/Roaming/Typora/typora-user-images/image-20200529152434229.png)]

以下是本人项目中使用总结:

实体类



@Entity
@Table(name = "signedorder")
@Getter
@Setter
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class SignedOrder {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer id;//id
    @CreatedDate
    @Column(updatable = false)
    private Date createTime;//创建时间
    @LastModifiedDate
    @Column
    private Date lastModifiedTime;//修改时间
    @ManyToOne(fetch = FetchType.EAGER, cascade = {
            CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH
    })
    @JoinTable(name = "staff_signedorder", joinColumns = @JoinColumn(name =
            "signedorder_id"), inverseJoinColumns = @JoinColumn(name = "staff_id"))
    private Staff staff;//所属用户
    @JoinColumn(name = "industry_id")
    private Integer industryId;//行业Id
}


@Entity
@Table(name = "staff")
@Getter
@Setter
@NoArgsConstructor
public class Staff {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;//id
    @Column(name = "name", length = 25)
    private String name;//姓名
    @JoinColumn(name = "city_id")
    private Integer cityId;//城市id


@Entity
@Table(name = "city")
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
public class City {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;//id
    @Column(name = "name", length = 50)
    private String name;//名称
}
//行业表和城市表一致,就不展示了

注解解释

实体类中相关注解解释:

  • @Entity: 对实体注释。任何Hibernate映射对象都要有这个注释
  • @Table: 声明此对象映射到数据库的数据表,该注释不是必须的,如果没有则系统使用默认值(实体的短类名)
  • @Getter @Setter@NoArgsConstructor:lombok提供注解,get、set方法及无参构造
  • @EntityListeners(AuditingEntityListener.class):加上此注解,时间注解@LastModifiedDate 和 @CreatedDate才可以生效
  • @Id: 声明此属性为主键
  • @GeneratedValue(strategy = GenerationType.IDENTITY):指定主键,

TABLE:使用一个特定的数据库表格来保存主键;

IDENTITY:主键由数据库自动生成(主要是自动增长型);

SEQUENCR:根据底层数据库的序列来生成主键,条件是数据库支持序列;

AUTO:主键由程序控制

  • @CreatedDate(updatable = false):创建时间时间字段,在insert的时候,会设置值;update时时间不变
  • @LastModifiedDate:修改时间段,update时会修改值
  • @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}): 多对一,

FetchType.EAGER:立即加载, 获取关联实体;

CascadeType.MERGE: 级联更新;

CascadeType.PERSIST:级联新建;

CascadeType.REFRESH:级联刷新

  • @JoinTable: JoinColumn:保存关联关系的外键的字段;inverseJoinColumns:保存关系的另外一个外键字
  • @Column:用来标识实体类中属性与数据表中字段的对应关系

测试类


@RunWith(SpringRunner.class)
@SpringBootTest
public class SprinBootMarketingsystemApplicationTests {
    @PersistenceContext//jpa的数据库操作类
    private EntityManager entityManger;
    @Test
    public void queryDb(){
       //给参数赋值
        Integer cityId = 1;
        Integer industryId = 2;
        Integer staffId = 16;
        Date startTime = DateUtil.parse("1970-01-01");//字符串时间转换为date类型
        Date endTime = Calendar.getInstance().getTime();//获取系统当前时间装换为date类型
        
        //创建SQL语句主体
        StringBuffer stringBuffer = new StringBuffer("\tSELECT\n" +
                "\tcount( * ) count,\n" +
                "\tci.NAME cityName\n" +
                "\tFROM\n" +
                "\tsignedorder s\n" +
                "\tLEFT JOIN staff_signedorder t ON s.id = t.signedorder_id\n" +
                "\tLEFT JOIN staff sta ON t.staff_id = sta.id\n" +
                "\tLEFT JOIN city ci ON sta.city_id = ci.id\n" +
                "\tWHERE\n" +
                "\t1 = 1");
         Map<String,Object> map =  new HashMap<>();
         //拼接动态参数
        if(industryId != null){
            
             
             //industryId代表传进来的参数名称,给参数赋值nativeQuery.setParameter("industryId", industryId);
             stringBuffer.append(" and s.industry_id = :industryId");
             map.put("industryId",industryId);
        }
        if(cityId != null){
            stringBuffer.append(" and ci.id = :cityId");
            map.put("cityId",cityId);
        }
        if(staffId != null){
            stringBuffer.append(" and sta.id = :staffId");
            map.put("staffId",staffId);
        }
        if(startTime!=null && endTime!=null){
            //使用这种赋值方式,时间类型需要给三个参数,参数名称,参数值,特定映射的类型TemporalType.DATE
            //nativeQuery.setParameter("create_time", startTime,TemporalType.DATE);
            stringBuffer.append( " and s.create_time BETWEEN :startTime and :endTime ");
            map.put("startTime",startTime);
            map.put("endTime",endTime);
        }
        Query nativeQuery = entityManger.createNativeQuery(stringBuffer.toString());
        for (String key : map.keySet()) {
            nativeQuery.setParameter(key, map.get(key));
        }
        //三种接受返回结果方式(第一种方式)
        
       //第二种方式和第一种方式相似
        
        //第三种方式:实体类接受
        nativeQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(TestVo.class));
        List<TestVo> resultList = nativeQuery.getResultList();
        for (TestVo svo:resultList
        ) {
            System.out.println(svo.toString());
        }
    }

打印结果

第一种方式打印结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iB3CVBpD-1590984326744)(ceshi1.png)]

第二种方式打印结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHOgQW55-1590984326745)(ceshi2.png)]

第三种方式打印结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JTMsc780-1590984326747)(ceshi3.png)]

TestVo实体接收类


@Data
public class TestVo {
    private String cityName;//城市名字
    private BigInteger count;//签单数量(必须使用BigInteger类型接受)
}

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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JPA如何使用nativequery多表关联查询返回自定义实体类

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

JPA怎么使用nativequery多表关联查询返回自定义实体类

这篇文章主要介绍了JPA怎么使用nativequery多表关联查询返回自定义实体类,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。JPA nativequery多表关联查询返回
2023-06-25

Spring Data Jpa多表查询如何返回自定义实体

小编给大家分享一下Spring Data Jpa多表查询如何返回自定义实体,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!SpringDataJpa多表查询返回自定
2023-06-29

怎么使用AOP+反射实现自定义Mybatis多表关联查询

这篇“怎么使用AOP+反射实现自定义Mybatis多表关联查询”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么使用AOP+
2023-06-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录