使用JPA自定义VO类型转换(EntityUtils工具类)

目录
  • JPA自定义VO类型转换(EntityUtils工具类)
  • dto,vo,po,bo等实体转换工具类
    • 下面宣布这次的主角:dozer

JPA自定义VO类型转换(EntityUtils工具类)

在JPA查询中,如果需要返回自定义的类,可以使用EntityUtils工具类,该类源码:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 954L
 * @create 2019/10/30 17:27
 */
public class EntityUtils {

    private static final Logger log = LoggerFactory.getLogger(EntityUtils.class);

    /**
     * 将数组数据转换为实体类
     * 此处数组元素的顺序必须与实体类构造函数中的属性顺序一致
     *
     * @param list  数组对象集合
     * @param clazz 实体类
     * @param <T>   实体类
     * @param model 实例化的实体类
     * @return 实体类集合
     */
    public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz, Object model) {
        List<T> returnList = new ArrayList<T>();
        if (list.isEmpty()) return returnList;
        Object[] co = list.get(0);
        List<Map> attributeInfoList = getFiledsInfo(model);
        Class[] c2 = new Class[attributeInfoList.size()];
        if (attributeInfoList.size() != co.length) {
            return returnList;
        }
        for (int i = 0; i < attributeInfoList.size(); i++) {
            c2[i] = (Class) attributeInfoList.get(i).get("type");
        }
        try {
            for (Object[] o : list) {
                Constructor<T> constructor = clazz.getConstructor(c2);
                returnList.add(constructor.newInstance(o));
            }
        } catch (Exception ex) {
            log.error("实体数据转化为实体类发生异常:异常信息:{}", ex.getMessage());
            return returnList;
        }
        return returnList;
    }

    private static Object getFieldValueByName(String fieldName, Object modle) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = modle.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(modle, new Object[]{});
            return value;
        } catch (Exception e) {
            return null;
        }
    }

    private static List<Map> getFiledsInfo(Object model) {
        Field[] fields = model.getClass().getDeclaredFields();
        List<Map> list = new ArrayList(fields.length);
        Map infoMap = null;
        for (int i = 0; i < fields.length; i++) {
            infoMap = new HashMap(3);
            infoMap.put("type", fields[i].getType());
            infoMap.put("name", fields[i].getName());
            infoMap.put("value", getFieldValueByName(fields[i].getName(), model));
            list.add(infoMap);
        }
        return list;
    }
}

使用原生sql查询:

    /**
     * 根据公司名称查询岗位
     * @param name 公司名称
     * @return  List<EmploymentPosition>
     */
    @Query(value = "select id, position, salary, people, experience, address, update_time from employment_position where company_name = ?1 and is_delete is false ORDER BY sort asc", nativeQuery = true)
    List<Object[]> findByCompanyName(String name);

使用类型转换:

@Override
    public List<EmploymentPositionVO> findByCompanyName(String name) {
        List<Object[]> objects = employmentPositionRepository.findByCompanyName(name);
        return EntityUtils.castEntity(objects, EmploymentPositionVO.class, new EmploymentPositionVO());
    }

VO类如下:

import lombok.Data;
import java.math.BigInteger;
import java.sql.Timestamp;

/**
 * @website https://el-admin.vip
 * @description /
 * @author budezhenjia
 * @date 2021-02-08
 **/
@Data
public class EmploymentPositionVO {

    /** ID */
    private BigInteger id;

    /** 岗位名称 */
    private String position;

    /** 月薪 */
    private String salary;

    /** 人数 */
    private Integer people;

    /** 工作经验 */
    private String experience;

    /** 工作地点 */
    private String address;

    /** 更新时间 */
    private Timestamp updateTime;
    public EmploymentPositionVO(BigInteger id, String position, String salary, Integer people, String experience, String address, Timestamp updateTime) {
        this.id = id;
        this.position = position;
        this.salary = salary;
        this.people = people;
        this.experience = experience;
        this.address = address;
        this.updateTime = updateTime;
    }
    public EmploymentPositionVO() {
    }
}

注意!

查询sql语句所查询出来的字段应与VO类中属性顺序一致,类型也需要一致!!

例如ID这个字段MySQL中类型为bigint,VO类中的类型为bigInteger

dto,vo,po,bo等实体转换工具类

3层开发以及不是多么新颖的开发思想了,但是呢,苦于开发的程序猿们,经常会被各个实例之间的转换弄得晕头转向,尤其是转换的次数多了,一下就蒙了,不知道转到哪里去了,博主也有这种困难,于是在网上到处找,找到了一些方法,并结合自身的开发使用,填补一些坑,希望对大家有所帮助!

下面宣布这次的主角:dozer

他是谁,一看英文名就不懂吧,其实他是一个大家都知道的一个角色,spring里面他可是家喻户晓的一个主角,没错就是beanUtils(其实,就是他的替身!)主要作用就是用来复制 JavaBean 属性的类库,什么叫复制,没错,就一模一样的再来一份,但是这样有一点点小小的区别,那就是,在使用的时候,需要指定一个“容器”(瞎说的,就是一个映射接受对象,也可以叫做目标)来存放,不然,复制到哪去,是不是。

这个时候呀,就有一个经纪人的出现,需要通过“经纪人”去代理复制,才能叫这个替身出来呀(专业替身30年,必须要有经纪人)

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.5.1</version>
</dependency>

好了,经纪人的名片已经发出,这个时候,找到剧组,咱们看看这个替身能不能胜任,能应用在哪些剧组,哪些场景!

第一种场景,完全一样:(咦,度一样了,那肯定好弄,又看不出来区别)

在各个实体是一样的情况下,是很容易的,直接上不就行啦,也不用做啥处理是不是:

直接是用原始替身(API方法)

 Mapper mapper = new DozerBeanMapper();
DestinationObject destObject =
    mapper.map(sourceObject, DestinationObject.class);

嘿嘿,换一下衣服,直接OK了(也就是使用mapper转换复制)

第二种场景,完全不一样:

求乞,完全不一样,这咋的弄呀,肯定得换替身,是不是!最起码找个相似的嘛(不用担心,咱们有化妆师呀,化妆走起!)

@Data
public class UserVo{
    @Mapping("userName")
    private String name;
    @Mapping("password")
    private String pwd;
}

看一下,是不是这个东西,好眼熟,没错,是咱们的vo

@Data
@TableName("user")
public class UserEntity implements Serializable {
    @ApiModelProperty(value = "id")
    @TableId(value = "id", type = IdType.INPUT)
    private String id;
    @ApiModelProperty(value = "用户名")
    @Mapping("name")
    private String userName;
    @ApiModelProperty(value = "密码")
    @Mapping("pwd")
    private String password;
    @ApiModelProperty(value = "登录名")
    private String loginName;
    @ApiModelProperty(value = "创建时间")
    private Date createTime;
    @ApiModelProperty(value = "修改时间")
    private Date updateTime;
    @ApiModelProperty(value = "版本号")
    private Integer version;
    @ApiModelProperty(value = "作废标记")
    private Integer deleted;
}

这个呢,是咱们的实体对象(也就是数据库对象了)

看一下,是不是发小完全不一样呀!

这里呢,@Mappin充当了化妆品的角色,将每一个不同的细节进行遮盖,使其成为和原来的实例一模一样(也就是做了映射了)

 @Mapping("userName")
 private String name;

上面就是一个化妆处理,将name映射为userName

每一个地方处理完成后,直接上剧组,看看能不能不被发现

    /**
     * 转换实体为另一个指定的实体
     * 任意一个参数为NULL时 会抛出NPE
     *
     * @param source 源实体 不能为NULL
     * @param clazz 目标实体 不能为NULL
     * @param <T> 泛型
     * @return 转换后的结果
     */
    @NonNull
    public static <T> T convert(@NonNull Object source, @NonNull Class<T> clazz) {
        return  dozerMapper.map(source, clazz);
    }

实例:

 UserEntity userEntity = userMapper.selectOne(wrapper);
            UserVo convert = null;
            if(userEntity != null){
               convert = Dozer.convert(userEntity, UserVo.class);
            }

没错,这样就完成转换替身的操作了,结果是可以映射上的,各位可以试试哦!

第三种场景,一堆的不相同的替身(好难呀,一堆的不一样,不能找几个差不多的吗?)

针对于这种一堆的不一样的替身,dozer也有办法,通过JAVA8的stream流来进行化妆(化妆师牛,只能这么说)

 /**
     * 转换List实体为另一个指定的实体
     * source如果为NULL 会使用空集合
     * 在目标实体为NULL时 会抛出NPE
     *
     * @param source 源集合 可以为NULL
     * @param clazz 目标实体 不能为NULL
     * @param <T> 泛型
     * @return 转换后的结果
     */
    @Nullable
    public static <T> List<T> convert(@Nullable List<?> source, @NonNull Class<T> clazz) {
        return Optional.ofNullable(source)
                .orElse(Collections.emptyList())
                .stream()
                .map(bean -> dozerMapper.map(bean, clazz))
                .collect(Collectors.toList());
    }

看见没,这样一弄,就OK了,咦,是不是发现化妆师dozerMapper从哪来的(这么牛的化妆师,召几个开化妆店去),博主告诉你,这个化妆师呀,并不是越多也好的(大家都知道,一样的对象,建立太多浪费空间),只需要建立一个全局唯一的就行了,博主带你们看一下化妆间就明白了(工具类来了)

package cn.yichehuoban.ycbb.platform.util.beanutils;
import org.dozer.Mapper;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Component
public class Dozer {
    /**
     * dozer转换的核心mapper对象
     */
    public static final Mapper dozerMapper = new org.dozer.DozerBeanMapper();
    /**
     * 转换实体为另一个指定的实体
     * 任意一个参数为NULL时 会抛出NPE
     *
     * @param source 源实体 不能为NULL
     * @param clazz 目标实体 不能为NULL
     * @param <T> 泛型
     * @return 转换后的结果
     */
    @NonNull
    public static <T> T convert(@NonNull Object source, @NonNull Class<T> clazz) {
        return  dozerMapper.map(source, clazz);
    }
    /**
     * 转换List实体为另一个指定的实体
     * source如果为NULL 会使用空集合
     * 在目标实体为NULL时 会抛出NPE
     *
     * @param source 源集合 可以为NULL
     * @param clazz 目标实体 不能为NULL
     * @param <T> 泛型
     * @return 转换后的结果
     */
    @Nullable
    public static <T> List<T> convert(@Nullable List<?> source, @NonNull Class<T> clazz) {
        return Optional.ofNullable(source)
                .orElse(Collections.emptyList())
                .stream()
                .map(bean -> dozerMapper.map(bean, clazz))
                .collect(Collectors.toList());
    }
}

在给大家看一下我们的替身

@Data
public class UserVo{
    @Mapping("userName")
    private String name;
    @Mapping("password")
    private String pwd;
    private String loginName;
    private Integer version;
    private Integer deleted;
    private String id;
}

一样的地方直接就映射上了,不一样的地方使用 @Mapping注解,填写上源指定对象的字段名就行

这只是几种方法,还有其他双向映射,数据拷贝等,可以看看他的官方文档dozer

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

(0)

相关推荐

  • 使用Spring Data JPA的坑点记录总结

    前言 Spring-data-jpa的基本介绍:JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式,百度百科说是JDK为了实现ORM的天下归一,目前也是在按照这个方向发展,但是还没能完全实现.在ORM框架中,Hibernate是一支很大的部队,使用很广泛,也很方便,能力也很强,同时Hibernate也是和JPA整合的比较良好,我们可以认为JPA是标准,事实上也是,JPA几乎都是接口,实现都是Hibernate在做,宏观上面看,在JPA的统一之下Hibernate很良好的运行. 最近在

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

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

  • springboot 之jpa高级查询操作

    springboot的jpa可以根据方法名自动解析sql 非常方便, 只需要在 dao接口中定义方法即可; 下面是一个 demo package com.bus365.root.dao; import java.io.Serializable; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.reposi

  • 使用JPA自定义VO类型转换(EntityUtils工具类)

    目录 JPA自定义VO类型转换(EntityUtils工具类) dto,vo,po,bo等实体转换工具类 下面宣布这次的主角:dozer JPA自定义VO类型转换(EntityUtils工具类) 在JPA查询中,如果需要返回自定义的类,可以使用EntityUtils工具类,该类源码: import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.

  • java自定义封装StringUtils常用工具类

    自定义封装StringUtils常用工具类,供大家参考,具体内容如下 package com.demo.utils; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 字符串操作工具类 * @author dongyangyang * @Date 2016/12/28 23:12 * @Version 1.0 * */ public class StringUtils { /** * 首字

  • 使用JPA自定义VO接收返回结果集(unwrap)

    目录 JPA自定义VO接收返回结果集(unwrap) JPA返回自定义VO JPA自定义VO接收返回结果集(unwrap) JPA跟mybitis比较,简单的业务搜索是方便的,但是设计到复杂的SQL搜索时,我们需要自定义SQL. 1.@Query直接写SQL,缺点是无法动态的组装条件 2.JPA的Specification对象动态组装where搜索条件 3.entityManager执行CriteriaBuilder 4.entityManger直接使用createNativeQuery,执行原

  • java字符串与日期类型转换的工具类

    常用的字符串转date,和日期转字符串的方法,具体内容如下 package com.cq2022.zago.base.util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; import javax.xml.datatype.Dat

  • 超实用的android自定义log日志输出工具类

    android自定义log日志输出工具,该工具类具有以下优点: 1 在LogUtlis方法的第一个参数中填this可以输出当前类的名称,特别是在匿名内部类使用也可以输出当前类名. 如 : LogUtils.i(this,"这是一个实用的日志工具类") 或 LogUtils.i(类名.class,"这是一个实用的日志工具类"). 效果:比如我在MainActivity中直接LogUtils.i(this,"logTest"),配合自己喜欢的标志,结

  • JPA 使用criteria简单查询工具类方式

    目录 使用criteria简单查询工具类 首先创建类并实现Specification<T>接口 新建ExpandCriterion接口 使用criteria前提是dao接口必须实现JpaSpecificationExecutor<T>接口 打包JPA动态查询(CriteriaQuery) eq.ge.gt 封装JPA动态查询(CriteriaQuery) EntityManager管理器,通过spring管理 Page分页和结果封装类 IBaseDao接口实现了BaseDaoImp

  • JPA 使用criteria简单查询工具类方式

    目录 打包JPA动态查询(CriteriaQuery) eq.ge.gt 封装JPA动态查询(CriteriaQuery) EntityManager管理器,通过spring管理 Page分页和结果封装类 IBaseDao接口实现了BaseDaoImpl IBaseDao接口 以前用jpa写了一个条件筛选的查询数据如下,才知道那么渣渣,就是一个表,根据前端来筛选数据,写的如下 首先就是判断前端传来的参数就写了那么多,现在才发现是渣渣中的渣渣,而且还费时,用criteria很快就搞定 首先创建类并

  • Java中StringUtils工具类进行String为空的判断解析

    判断某字符串是否为空,为空的标准是str==null或str.length()==0 1.下面是StringUtils判断是否为空的示例: StringUtils.isEmpty(null) = true StringUtils.isEmpty("") = true StringUtils.isEmpty(" ") = false //注意在 StringUtils 中空格作非空处理 StringUtils.isEmpty(" ") = fals

  • Spring JPA的实体属性类型转换器并反序列化工具类详解

    目录 一.JPA单体JSON与Map的映射 创建一个转换类 只需在模型类上加个注解就能完成自动转换 二.封装反序列化工具类 利用JPA的AttributeConverter接口实现属性转换过于局限 如何调用自定义的转换器 一.JPA 单体JSON与Map的映射 数据库中test字段为json类型 {"key": "颜色", "value": "深白色", "key_id": 1, "value_i

  • 自定义Toast工具类ToastUtil防止多次点击时Toast不消失的方法

    有时候我们点击一个按钮出现toast但是当不小心多次点击时,toast会重复出现,这时候通过下面的ToastUtil类可以实现不小心多次点击的问题. public class ToastUtil { /* private Context context; public ToastUtil(Context context) { this.context=context; }*/ private static Toast toast; public static void showToast(Con

随机推荐