Spring Data Jpa---EntityManager属性与属性值的映射

在开发中使用JPA作为项目的持久层框架,在联表查询查询的时候多半会用到EntityManager这个类。

String sql = "SELECT xxx, xxx, xxx FROM x a JOIN y b ON x.id = y.id";
Query nativeQuery = em.createNativeQuery(sql);
List<Object[]> resultList = nativeQuery.getResultList();

大概是这种形式,Object数组里面的值与sql里面写的列名对应。

for (Object[] item : resultList) {
    类 obj = new 类();
    obj.setXXX(item[0]);
    obj.setYYY(item[1]);
    ...
}

最后一个一个的赋值到类的属性里面去,如果你查询的字段有二三十个的话,赋值语句将会有二三十条(不想写这么多,而且还可能写漏)

 

但是,查询的结果与你select的列一致,而我们类属性的定义一般也跟数据库表的列一致,只不过Java里面使用小驼峰,数据库使用下划线。

既然一致,我们就可以通过反射为我们Java类的属性赋值,而不用一个一个的去set。

 

这是赋值用到的工具类,可以直接拿来用

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @auther: lkz
 * @date: 2021/02/27
 * @version: 1.0
 * @description:
 */
public class BeanUtil {

    /**
     * source的值顺序需要与columns的列名对应
     * @param columns select语句选择的列
     * @param target 要赋值的对象的类型
     * @param source 数据
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static Object copyProperties(String columns, Class target, Object[] source) {
        return copyProperties(columns, null, target, source);

    }

    /**
     *
     * @param columns
     * @param prefix 只映射columns中以${prefix}开头的列
     * @param target
     * @param source
     * @return
     */
    public static Object copyProperties(String columns, String prefix, Class target, Object[] source) {
        // 去空格 根据逗号分割
        String[] tempArr = columns.replace(" ", "").split(",");

        List<String> columnNameArr = new ArrayList<>();
        for (String item : tempArr) {
            int index = -1;
            while ((index = item.indexOf('_')) != -1) {
                if (index != -1) {// 如果有下划线 把下划线后面的字母变为大写
                    StringBuilder stringBuilder = new StringBuilder(item);
                    // 转为大写 英文字母大写和小写的ASCII码差了32
                    // 根据这个性质可以使用异或 无需判断
                    char ch = (char) (stringBuilder.charAt(index + 1) ^ 32);
                    item = stringBuilder.replace(index, index + 2, String.valueOf(ch)).toString();
                }
            }
            columnNameArr.add(item);
        }

        // 实例化该类型
        Object o = null;
        try {
            o = target.newInstance();
        } catch (InstantiationException e) {
            System.out.println("实例化出现错误");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            System.out.println("参数错误");
            e.printStackTrace();
        }

        // 赋值
        for (int i = 0; i < columnNameArr.size(); ++i) {
            String columnName = columnNameArr.get(i);
            int index = -1;
            if (prefix != null) {
                if (columnName.startsWith(prefix)) {
                    // 删除前缀
                    columnName = columnName.replaceFirst(prefix, "");
                } else {
                    continue;
                }
            }

            try {
                o = getAndSet(columnName, target, o, source[i]);
            } catch (NoSuchFieldException e) {
                // 第一次找不到先去找父类
                // 一般我们会把表中id、created_time、created_by、modified_by、modified_time这些字段定义在BaseEntity
                // 其他类继承BaseEntity,可以减少代码重复
                // 这样的话有一些找不到的字段就有必要去父类找找
                // 实际开发过程中 与数据库对应的实体类不会有很多层级 不会有 曾祖 -》 租 -》 父 - 》 子
                // 最多 父 -》 子
                // 这里默认去找一下上一层的父类
                // 如果有多个层级 需要循环去找所以父类
                try {
                    o = getAndSet(columnName, target.getSuperclass(), o, source[i]);
                } catch (NoSuchFieldException noSuchFieldException) {
                    System.out.println("在类型 " + target +" 中, 没有属性 " + columnName);
                }
            }

        }
        return o;
    }

    private static Object getAndSet(String columnName, Class target, Object entity, Object source) throws NoSuchFieldException {
        // 数据库的bigint对应java的BigInteger 无法直接转为Long
        if (source instanceof BigInteger) {
            source = ((BigInteger) source).longValue();
        }
        try {
            // 能获取类上公有、保护、私有字段 但是不能获取父类字段
            Field field = target.getDeclaredField(columnName);

            if (Modifier.isStatic(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
                System.out.println("忽略static或者final修饰的字段, " + target + ", " +  columnName);
                return entity;
            }
            // 设置字段为可访问行
            field.setAccessible(true);
            field.set(entity, source);
        } catch (IllegalAccessException e) {
            System.out.println("类型赋值错误");
            e.printStackTrace();
        }
        return entity;
    }




}

 

演示

首先建立两张表

test_a

/*
 Navicat Premium Data Transfer

 Source Server         : 本地
 Source Server Type    : MySQL
 Source Server Version : 80022
 Source Host           : localhost:3306
 Source Schema         : test

 Target Server Type    : MySQL
 Target Server Version : 80022
 File Encoding         : 65001

 Date: 06/03/2021 16:36:12
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for test_a
-- ----------------------------
DROP TABLE IF EXISTS `test_a`;
CREATE TABLE `test_a`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `num` int(0) NULL DEFAULT NULL,
  `str_x_y` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `time` datetime(0) NULL DEFAULT NULL,
  `time1` date NULL DEFAULT NULL,
  `double_num` double NULL DEFAULT NULL,
  `float_num` float NULL DEFAULT NULL,
  `message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of test_a
-- ----------------------------
INSERT INTO `test_a` VALUES (1, 1, 'aaa', '2021-03-06 13:57:49', '2021-03-06', 1, 1, 'aaaaa');
INSERT INTO `test_a` VALUES (2, 2, 'bbb', '2021-03-06 13:58:19', '2021-03-06', 2, 2, 'bbbbbb');
INSERT INTO `test_a` VALUES (3, 3, 'ccc', '2021-03-06 13:58:44', '2021-03-06', 3, 3, 'cccccc');
INSERT INTO `test_a` VALUES (4, 4, 'ddd', '2021-03-06 13:59:03', '2021-03-06', 4, 4, 'ddddd');

SET FOREIGN_KEY_CHECKS = 1;

test_b

/*
 Navicat Premium Data Transfer

 Source Server         : 本地
 Source Server Type    : MySQL
 Source Server Version : 80022
 Source Host           : localhost:3306
 Source Schema         : test

 Target Server Type    : MySQL
 Target Server Version : 80022
 File Encoding         : 65001

 Date: 06/03/2021 16:37:21
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for test_b
-- ----------------------------
DROP TABLE IF EXISTS `test_b`;
CREATE TABLE `test_b`  (
  `id` bigint(0) NOT NULL AUTO_INCREMENT,
  `str_x` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of test_b
-- ----------------------------
INSERT INTO `test_b` VALUES (1, 'aa');
INSERT INTO `test_b` VALUES (2, 'bb');
INSERT INTO `test_b` VALUES (3, 'cc');
INSERT INTO `test_b` VALUES (4, 'dd');

SET FOREIGN_KEY_CHECKS = 1;

 

两个实体类

@Data
public class TestA {

    private Long id;

    private Integer num;

    private String strXY;

    private Date time;

    private Date time1;

    private Double doubleNum;

    private Float floatNum;

    private String message;


}
@Data
public class TestB {

    private Long id;

    private String strX;

}

 

现在要做的就是两张表的联查

因为列名重复的话要取别名,我们映射的话就不需要。

所以copyColumns和exeColumns是有区别的,执行用了exeColumns,映射用了copyColumns。

然后还给表取了别名,列名也加上了表名作为前缀,映射的时候要带上这个前缀才能正确处理它们之间的关系。

@RestController
public class controller {

    @Resource
    private EntityManager em;

    @GetMapping("/get")
    public List<Test> get() {

        String copyColumns = "a.id, a.num, a.str_x_y, a.time, a.time1, a.double_num, a.float_num, a.message, b.id, b.str_x ";
        String exeColumns = "a.id, a.num, a.str_x_y, a.time, a.time1, a.double_num, a.float_num, a.message, b.id bId, b.str_x ";
        String sql = "SELECT " + exeColumns + "FROM test_a a JOIN test_b b ON a.id = b.id";

        Query nativeQuery = em.createNativeQuery(sql);
        List<Object[]> resultList = nativeQuery.getResultList();

       List<Test> list = new ArrayList<>();

        for (int i = 0; i < resultList.size(); ++i) {
            TestA testA = (TestA) BeanUtil.copyProperties(copyColumns, "a.", TestA.class, resultList.get(i));
            TestB testB = (TestB) BeanUtil.copyProperties(copyColumns, "b.", TestB.class, resultList.get(i));
            Test test = new Test();
            test.setA(testA);
            test.setB(testB);
            list.add(test);
        }



        return list;

    }

}

注意

结果:数据都赋值到正确的属性上面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值