利用SpringBoot手写属于自己的ORM框架

springcloud 专栏收录该内容
3 篇文章 0 订阅

用springboot手写自己的ORM框架

手写之前让我们简单回顾一下ORM框架,就是java对象和数据库字段之间的转化,当我们封装好了ORM之后,我们就可以直接操作对象,而不用管后台和数据库打交道的一些细节。

但是如果我们要操作对象,那么对象和数据库表,字段如何映射的呢,想想hibernate和mybatis是不是在实体中加入一些注解呢,也有xml配置的,本文中主要讲注解。废话有点多,直接开撸

既然我们要在实体中加入以下注解,总不能用mybatis和hibernate的注解吧,那么我们就自定义一些注解,帮我们做实体和字段的映射。

我们需要自定义两个注解,表名映射的注解和字段名映射的注解,因ID是字段中比较特殊的,所以我们自定义一个ID的注解,详见如下的代码

自定义注解

先定义表名注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 *@Description  自定义注解,用来表示类和数据库表的关系
 *@Author dym
 *@Date 2019/8/30 15:05
 *@Param
 *@return
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MuyanTableName {

    String tableName() default "";
}

在定义字段注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *@Description  自定义字段和属性的映射关系注解
 *@Author dym
 *@Date 2019/8/30 15:07
 *@Param
 *@return
 */
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.FIELD )
public @interface MuyanColumn {

    String colomnName() default "";
}

最后定义id注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *@Description  自定义主键的注解
 *@Author dym
 *@Date 2019/8/30 15:08
 *@Param
 *@return
 */
@Retention( RetentionPolicy.RUNTIME )
@Target( ElementType.FIELD )
public @interface MuyanId {
    String idName() default "";
}

将定义好的注解,使用到我们的实体类上

import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanColumn;
import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanId;
import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanTableName;
import lombok.Data;
import lombok.ToString;

/**
 * @ClassName Student
 * @Description 实体类,这里采用自定义注解
 * @Author dym
 * @Date 2019/8/30 14:57
 * @Version 1.0
 **/
@Data
@MuyanTableName(tableName="student")
@ToString
public class Student {

    @MuyanId(idName = "id")
    private Integer id;

    @MuyanColumn(colomnName = "name")
    private String name;

    @MuyanColumn(colomnName = "age")
    private Integer age;

    @MuyanColumn(colomnName = "sex")
    private String sex;
}

是不是感觉像模像样的,有点那个意思

完了就是老一套,写我们的controller和service,还有mapper,这都不是重点,所以我们很快撸完

controller

import com.muyan.springboot.myspringtransaction.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.sql.SQLException;

/**
 * @ClassName StudentController
 * @Description 学生控制层
 * @Author dym
 * @Date 2019/8/30 8:58
 * @Version 1.0
 **/
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private UserService userService;

    @RequestMapping("/addUser")
    public void addData() throws SQLException {
        userService.addData();
    }
}

service

import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanTransction;
import com.muyan.springboot.myspringtransaction.chg.domain.OptionLog;
import com.muyan.springboot.myspringtransaction.chg.domain.Student;
import com.muyan.springboot.myspringtransaction.chg.mapper.LogMapper;
import com.muyan.springboot.myspringtransaction.chg.mapper.StudentMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * @ClassName ChangerUserService
 * @Description 学生业务类
 * @Author dym
 * @Date 2019/8/30 10:16
 * @Version 1.0
 **/
@Service
public class ChangerStudentService {

    @Autowired
    private StudentMapper studentMapper;

    /**
     *@Description  保存实体
     *@Author dym
     *@Date 2019/9/3 17:53
     *@Param [student]
     *@return boolean
     */
    @MuyanTransction
    public boolean saveDataByOrm(Student student){
        boolean result = studentMapper.add( student );
        return result;
    }

    /**
     *@Description  保存编辑实体
     *@Author dym
     *@Date 2019/9/3 17:54
     *@Param [student]
     *@return boolean
     */
    @Transction
    public boolean update(Student student){
        boolean result = studentMapper.update( student );
        return result;
    }

    /**
     *@Description  根据id删除实体
     *@Author dym
     *@Date 2019/9/3 17:55
     *@Param [id]
     *@return boolean
     */
    @Transction
    public boolean delete(int id){
        boolean result = studentMapper.delete( id );
        return result;
    }

    /**
     *@Description  根据ID查询student实体
     *@Author dym
     *@Date 2019/9/3 17:56
     *@Param [id]
     *@return com.muyan.springboot.myspringtransaction.chg.domain.Student
     */
    public Student query(int id){
        Student student = (Student) studentMapper.query( id );
        return student;
    }
    /**
     *@Description  查询所有的学生列表
     *@Author dym
     *@Date 2019/9/3 17:58
     *@Param []
     *@return java.util.List<com.muyan.springboot.myspringtransaction.chg.domain.Student>
     */
    public List<Student> queryAll(){
        List<Student> list = studentMapper.queryAll();
        return list;
    }
}

mapper

import com.muyan.springboot.myspringtransaction.chg.domain.Student;
import org.springframework.stereotype.Component;

/**
 * @ClassName StudentMapper
 * @Description student的mapper层
 * @Author dym
 * @Date 2019/9/2 9:51
 * @Version 1.0
 **/
@Component
public class StudentMapper {

}

那么我们如何写mapper呢,是不是在mapper里面就可以写对我们实体做增删改查了呢,可以是可以,但是我们手写的简单的ORM框架,既然是框架,那么我们就要封装,因为我们系统中不可能只有一个实体,不可能每个实体对应的mapper都写如何从映射中拿到值做操作数据库。所以我们需要一个通用的mapper来做这件事,所有的mapper都来继承它,这样以后如果新建一张表,新建一个实体之后,新建一个mapper直接继承,而不是再解析映射。

既然是通用mapper,那么我们建的mapper必须满足所有的实体,我们需要设计它为泛型。

新建一个baseMapper

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

import java.sql.*;
import java.util.*;

/**
 * @ClassName BaseMapper
 * @Description 定义抽象类,用来封装ORM数据映射
 * @Author dym
 * @Date 2019/9/2 9:43
 * @Version 1.0
 **/
public class BaseMapper<T> {
    /**
     *@Description  保存实体
     *@Author dym
     *@Date 2019/9/2 9:52
     *@Param [t]
     *@return boolean
     */
    public boolean add(T t){
        return null;

    }


    /**
     *@Description  删除某个实体
     *@Author dym
     *@Date 2019/9/2 9:54
     *@Param [id]
     *@return void
     */
    public boolean delete(Object id){
        return null;
    }


    /**
     *@Description  编辑某个实体
     *@Author dym
     *@Date 2019/9/2 9:54
     *@Param [t]
     *@return void
     */
    public boolean update(T t){
        return null;
    }


    /**
     *@Description  查询某个实体
     *@Author dym
     *@Date 2019/9/2 9:55
     *@Param [id]
     *@return T
     */
    public T query( Object id){
        return null;
    }

    /**
     *@Description  查询所有数据
     *@Author dym
     *@Date 2019/9/2 9:55
     *@Param []
     *@return java.util.List<T>
     */
    public List<T> queryAll(){
        return null;
    }

}

问题:我们新建完我们需要的所有类之后,我们开始本文中重点的重点,如果不做ORM框架,那么我们是不是就用jdbc做CURD了呢,不错,但是jdbc操作的是字段,而不是对象,我们传递进来的是对象,那怎么办呢!

思路:那么我们需要做个事,什么事呢,就是将对象转化为字段,此处分两步走,第一步每个方法里面都需要做个对象和字段的转化,第二步拿到字段之后,做JDBC操作。

有了这个思路我们是不是就可以着手做了呢,但是我们想想如果每个方法都做堆一堆这样的代码,估计看得人头有点大,所以我们统一写一个类(BuildParamToFieldManager)来帮助我们做专一的事,再写一个jdbc的封装类(MuyanJdbcTemplate)来帮助我们写jdbc具体的操作。因为我们不为具体某一个实体服务,所以统一采用泛型。

做具体操作前,我们需要先写一个工具,什么工具类呢,就是帮我们从实体中获取表名和字段名

注意:前方车要转弯,请系好安全带,ps:就是要懂一点java反射的知识,否则容易晕车

import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanColumn;
import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanId;
import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanTableName;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * @ClassName MuyanStringUtils
 * @Description 自定义工具类
 * @Author dym
 * @Date 2019/9/3 9:39
 * @Version 1.0
 **/
public class MuyanStringUtils {


    /**
     *@Description  将第一个字母替换为大写
     *@Author dym
     *@Date 2019/9/3 9:40
     *@Param [str]
     *@return java.lang.String
     */
    public static String firstUpperCase(String str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1, str.length());
    }

    /**
     *@Description  获取属性的set方法
     *@Author dym
     *@Date 2019/9/3 9:40
     *@Param [fieldName]
     *@return java.lang.String
     */
    public static String getSetMethod(String fieldName){
        return "set"+firstUpperCase( fieldName );
    }

    /**
     *@Description  根据实体获取表名称
     *@Author dym
     *@Date 2019/9/2 15:26
     *@Param [clazz]
     *@return java.lang.String
     */
    public static String getTableName(Class<?> clazz){
        //默认获取类中的小写名称
        String tableName  = clazz.getSimpleName();
        if (null != clazz){
            //判断类是否加载了自定义注解
            if (clazz.isAnnotationPresent( MuyanTableName.class)){
                MuyanTableName muyanTableName = clazz.getAnnotation( MuyanTableName.class );
                tableName = muyanTableName.tableName();

            }
        }
        return tableName;
    }

    /**
     *@Description  根据属性名称获取字符名称
     *@Author dym
     *@Date 2019/9/2 15:28
     *@Param [field]
     *@return java.lang.String
     */
    public static String getTableFieldName(Field field){
        String fieldName = "";
        if (null != field){
            if (field.isAnnotationPresent( MuyanColumn.class )){
                MuyanColumn annotation = field.getAnnotation( MuyanColumn.class );
                if (!Objects.equals("",annotation)){
                    fieldName  = annotation.colomnName();
                }
            }else if(!field.isAnnotationPresent( MuyanId.class )){
                fieldName = field.getName();
            }
        }
        return fieldName;
    }

    /**
     *@Description  根据字段获取主键的名称
     *@Author dym
     *@Date 2019/9/2 17:17
     *@Param [field]
     *@return java.lang.String
     */
    public static String getTableIdName(Field field){
        String idName = "";
        if (null != field){
            if (field.isAnnotationPresent( MuyanId.class )){
                MuyanId muyanId = field.getAnnotation( MuyanId.class );
                if (!Objects.equals( "",muyanId )){
                    idName = muyanId.idName();
                }

            }else if (!field.isAnnotationPresent( MuyanColumn.class )){
                idName = field.getName();
            }
        }
        return idName;

    }
}

那我们开始写BuildParamToFieldManager

手写我们写一个保存实体,就是往数据库中插一条数据

思路:传递进来的肯定是一个实体,我们通过实体拿到表名和字段名,完了拼接一个insert语句返回给baseMapper,由basemapper来执行jdbc操作

public void buildInsertFieldParams(T entity, StringBuffer sql, List<Object> paramsList) {
        //首先根据clazz获取表名称
        String tableName = MuyanStringUtils.getTableName( entity.getClass() );
        //其次开始拼接sql
        sql.append( "insert into " ).append( tableName ).append( " ( " );
        try {
            //获取字段名称
            Field[] fields = entity.getClass().getDeclaredFields();
            if (!Objects.equals( "", fields )){
                for (Field field : fields) {
                    //根据属性值获取数据库字段名称
                    String tableFieldName = MuyanStringUtils.getTableFieldName( field );
                    //因为属性有可能是private不能访问的,那么我们就通过强吻她
                    field.setAccessible( true );
                    //字段取到之后,开始将值填充参数列表中
                    Object o = field.get( entity );
                    if (o != null && !"".equals( tableFieldName )){
                        //继续拼接sql
                        sql.append( tableFieldName ).append( "," );
                        paramsList.add( o );
                    }
                }
                //去掉字段中最后一个逗号,
                sql.deleteCharAt( sql.length() - 1 ).append( " ) values ( " );
                for (Object o : paramsList) {
                    //sql.append( o ).append( "," );
                    sql.append( " ? ," );
                }
                //去掉占位符中的最后一个逗号
                sql.deleteCharAt( sql.length()  - 1 ).append( " ) " );

            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

修改的方法怎么写呢,其实原理一样,就是sql格式不一样而已

public void buildUpdateFieldParams(T entity, StringBuffer sql, List<Object> paramsList) {
        //首先根据clazz获取表名称
        String tableName = MuyanStringUtils.getTableName( entity.getClass() );
        //其次开始拼接sql
        sql.append( "update " ).append( tableName ).append( " set " );
        try {
            //获取字段名称
            Field[] fields = entity.getClass().getDeclaredFields();
            if (!Objects.equals( "", fields )){
                String idName = "";
                Object idValues = null;
                for (Field field : fields) {
                    //根据属性值获取数据库字段名称
                    String tableFieldName = MuyanStringUtils.getTableFieldName( field );
                    //因为属性有可能是private不能访问的,那么我们就通过强吻她
                    field.setAccessible( true );
                    if (StringUtils.isEmpty( idName )){
                        idName = MuyanStringUtils.getTableIdName( field );
                    }else{
                        //字段取到之后,开始将值填充参数列表中
                        Object o = field.get( entity );
                        if (o != null){
                            //继续拼接sql
                            sql.append( tableFieldName ).append( " = ?," );
                            paramsList.add( o );
                        }
                    }
                    if (field.isAnnotationPresent( MuyanId.class )){
                        idValues = field.get( entity );
                    }

                }
                //去掉字段中最后一个逗号,
                sql.deleteCharAt( sql.length() - 1 ).append( " where " + idName +" = ? ");
                paramsList.add( idValues );

            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

那查询呢,道理一样,继续前进

/**
     *@Description  拼接查询的语句
     *@Author dym
     *@Date 2019/9/2 18:10
     *@Param [clazz, sql]
     *@return void
     */
    public void buildSelectFieldParams(T entity, StringBuffer sql, Object primaryId, List<Object> paramList) {
        //首先根据clazz获取表名称
        String tableName = MuyanStringUtils.getTableName( entity.getClass() );
        //获取ID的名称
        String idName = "";
        sql.append( "select " );
        Field[] fields = entity.getClass().getDeclaredFields();
        if (null == fields){
            throw new RuntimeException( "获取属性值为空!" );
        }
        for (Field field : fields) {
            //因为属性有可能是private不能访问的,那么我们就通过强吻她
            field.setAccessible( true );
            //拼接主键名称
            if ("".equals( idName )){
                idName = MuyanStringUtils.getTableIdName( field );
                sql.append( idName ).append( "," );

            }
            //拼接其他字段的名称
            String tableFieldName = MuyanStringUtils.getTableFieldName( field );
            if (!"".equals( tableFieldName )){
                sql.append( tableFieldName ).append( "," );
            }

        }
        //去掉最后一个逗号
        sql.deleteCharAt( sql.length() - 1 ).append( " from " ).append( tableName );
        if (null !=  primaryId){
            sql.append( " where " ).append( idName ).append( " = ? " );
            paramList.add( primaryId );

        }

    }

删除也是同理

 public void buildDeleteFieldParams(Class<?> clazz, StringBuffer sql, List<Object> paramsList, Object id) {
        //获取表名
        String tableName = MuyanStringUtils.getTableName( clazz );
        //声明主键名称
        String idName = "";
        //获取实体类中的属性
        Field[] fields = clazz.getDeclaredFields();
        //循环查询出实体类中主键对应的数据库字段名称
        for (Field field : fields) {
            //因为属性有可能是private不能访问的,那么我们就通过强吻她
            field.setAccessible( true );
            if (Objects.equals( "",idName )){
                idName = MuyanStringUtils.getTableIdName( field );
            }else{
                break;
            }
        }
        if (Objects.equals( "", idName)){
            throw new RuntimeException( "实体类"+clazz.getSimpleName()+"中没有找到主键注解!" );
        }
        //将找到的主键名称拼接到删除的sql里面
        sql.append( "delete from " ).append( tableName ).append( " where " ).append( idName ).append( " = ? " );
        paramsList.add( id );
    }

有些传递的是实体T,有的传递是Class,通常我们做新增和修改的时候,是传递的实体,删除和单个查询传递的是某个整型,其实我们可以将这个整形或者字符串转成对象传递进来,如

新增

 Student student = new Student();
        student.setName("muyan456");
        student.setAge(40);
        student.setSex("男");
        boolean b = changerStudentService.saveDataByOrm( student );

删除

Student student = new Student();
        student.setId(37);
boolean result = changerStudentService.delete( student );

这样就很简单,如果我们增加点难度,我偏要不转对象,直接传递进来一个int或者String的id,那么我们框架如何支持呢,一个id我们很难知道是对应哪个实体,对应那张表。

思路:我们需要知道你调用的现在是继承baseMapper的哪个实体,乃至哪个具体的mapper在调用,这样我们就知道是哪个实体了,也就知道对应那张表了呀!

有了思路我们直接开干

public  Class<T>  getClassInfo( Class<? extends BaseMapper> clazz ){
        //获取本类
        //返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type (泛型类的参数化)
        // 比如说 StudentMapper extends BaseMapper<Student>
        //得到 BaseMapper<Student>的类型
        Type genericSuperclass = clazz.getGenericSuperclass();
        //判断获取超类中的类型是不是参数化类型
        if (genericSuperclass instanceof ParameterizedType) {
            //如果是参数化类型,则强制转化为参数化类型
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            //获取泛型中的实际类型,可能会存在多个泛型,例如Map<K,V>,所以会返回Type[]数组
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            //我们做的都是单表映射,并且必须有一个泛型,所以我们取第一个泛型就可以获取到具体泛型的类型
            Type actualTypeArgument = actualTypeArguments[0];
            //并且把这个具体的泛型强制转化为我们需要的带泛型的类(因为泛型的类型本身就是个类,那我们将强制)
            Class<T>  returnType = (Class<T>) actualTypeArgument;
            return returnType;
        }
        return null;
    }

所以我们的BuildParamToFieldManager类详细是这样的。

import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanId;
import com.muyan.springboot.myspringtransaction.chg.utils.MuyanStringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;

/**
 * @ClassName BuildParamToFieldManager
 * @Description 拼接增删改查的sql,从Object转化为数据库中的Field
 * @Author dym
 * @Date 2019/9/5 17:39
 * @Version 1.0
 **/
@Component
public class BuildParamToFieldManager<T> {

    /**
     *@Description  获取本类中的实体对象
     *@Author dym
     *@Date 2019/9/3 16:20
     *@Param []
     *@return java.lang.Class<T>
     */
    public  Class<T>  getClassInfo( Class<? extends BaseMapper> clazz ){
        //获取本类
        //返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type (泛型类的参数化)
        // 比如说 StudentMapper extends BaseMapper<Student>
        //得到 BaseMapper<Student>的类型
        Type genericSuperclass = clazz.getGenericSuperclass();
        //判断获取超类中的类型是不是参数化类型
        if (genericSuperclass instanceof ParameterizedType) {
            //如果是参数化类型,则强制转化为参数化类型
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            //获取泛型中的实际类型,可能会存在多个泛型,例如Map<K,V>,所以会返回Type[]数组
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            //我们做的都是单表映射,并且必须有一个泛型,所以我们取第一个泛型就可以获取到具体泛型的类型
            Type actualTypeArgument = actualTypeArguments[0];
            //并且把这个具体的泛型强制转化为我们需要的带泛型的类(因为泛型的类型本身就是个类,那我们将强制)
            Class<T>  returnType = (Class<T>) actualTypeArgument;
            return returnType;
        }
        return null;
    }


    /**
     *@Description  拼接删除的sql
     *@Author dym
     *@Date 2019/9/3 16:21
     *@Param [classInfo, sql, paramsList]
     *@return void
     */
    public void buildDeleteFieldParams(Class<?> clazz, StringBuffer sql, List<Object> paramsList, Object id) {
        //获取表名
        String tableName = MuyanStringUtils.getTableName( clazz );
        //声明主键名称
        String idName = "";
        //获取实体类中的属性
        Field[] fields = clazz.getDeclaredFields();
        //循环查询出实体类中主键对应的数据库字段名称
        for (Field field : fields) {
            //因为属性有可能是private不能访问的,那么我们就通过强吻她
            field.setAccessible( true );
            if (Objects.equals( "",idName )){
                idName = MuyanStringUtils.getTableIdName( field );
            }else{
                break;
            }
        }
        if (Objects.equals( "", idName)){
            throw new RuntimeException( "实体类"+clazz.getSimpleName()+"中没有找到主键注解!" );
        }
        //将找到的主键名称拼接到删除的sql里面
        sql.append( "delete from " ).append( tableName ).append( " where " ).append( idName ).append( " = ? " );
        paramsList.add( id );
    }


    /**
     *@Description  拼接查询的语句
     *@Author dym
     *@Date 2019/9/2 18:10
     *@Param [clazz, sql]
     *@return void
     */
    public void buildSelectFieldParams(T entity, StringBuffer sql, Object primaryId, List<Object> paramList) {
        //首先根据clazz获取表名称
        String tableName = MuyanStringUtils.getTableName( entity.getClass() );
        //获取ID的名称
        String idName = "";
        sql.append( "select " );
        Field[] fields = entity.getClass().getDeclaredFields();
        if (null == fields){
            throw new RuntimeException( "获取属性值为空!" );
        }
        for (Field field : fields) {
            //因为属性有可能是private不能访问的,那么我们就通过强吻她
            field.setAccessible( true );
            //拼接主键名称
            if ("".equals( idName )){
                idName = MuyanStringUtils.getTableIdName( field );
                sql.append( idName ).append( "," );

            }
            //拼接其他字段的名称
            String tableFieldName = MuyanStringUtils.getTableFieldName( field );
            if (!"".equals( tableFieldName )){
                sql.append( tableFieldName ).append( "," );
            }

        }
        //去掉最后一个逗号
        sql.deleteCharAt( sql.length() - 1 ).append( " from " ).append( tableName );
        if (null !=  primaryId){
            sql.append( " where " ).append( idName ).append( " = ? " );
            paramList.add( primaryId );

        }

    }

    /**
     *@Description  参数新增拼装
     *@Author dym
     *@Date 2019/9/2 15:36
     *@Param [aClass, sql, paramsList]
     *@return void
     */
    public void buildInsertFieldParams(T entity, StringBuffer sql, List<Object> paramsList) {
        //首先根据clazz获取表名称
        String tableName = MuyanStringUtils.getTableName( entity.getClass() );
        //其次开始拼接sql
        sql.append( "insert into " ).append( tableName ).append( " ( " );
        try {
            //获取字段名称
            Field[] fields = entity.getClass().getDeclaredFields();
            if (!Objects.equals( "", fields )){
                for (Field field : fields) {
                    //根据属性值获取数据库字段名称
                    String tableFieldName = MuyanStringUtils.getTableFieldName( field );
                    //因为属性有可能是private不能访问的,那么我们就通过强吻她
                    field.setAccessible( true );
                    //字段取到之后,开始将值填充参数列表中
                    Object o = field.get( entity );
                    if (o != null && !"".equals( tableFieldName )){
                        //继续拼接sql
                        sql.append( tableFieldName ).append( "," );
                        paramsList.add( o );
                    }
                }
                //去掉字段中最后一个逗号,
                sql.deleteCharAt( sql.length() - 1 ).append( " ) values ( " );
                for (Object o : paramsList) {
                    //sql.append( o ).append( "," );
                    sql.append( " ? ," );
                }
                //去掉占位符中的最后一个逗号
                sql.deleteCharAt( sql.length()  - 1 ).append( " ) " );

            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    /**
     *@Description  拼接update操作的sql
     *@Author dym
     *@Date 2019/9/2 17:07
     *@Param [clazz, sql, paramsList]
     *@return void
     */
    public void buildUpdateFieldParams(T entity, StringBuffer sql, List<Object> paramsList) {
        //首先根据clazz获取表名称
        String tableName = MuyanStringUtils.getTableName( entity.getClass() );
        //其次开始拼接sql
        sql.append( "update " ).append( tableName ).append( " set " );
        try {
            //获取字段名称
            Field[] fields = entity.getClass().getDeclaredFields();
            if (!Objects.equals( "", fields )){
                String idName = "";
                Object idValues = null;
                for (Field field : fields) {
                    //根据属性值获取数据库字段名称
                    String tableFieldName = MuyanStringUtils.getTableFieldName( field );
                    //因为属性有可能是private不能访问的,那么我们就通过强吻她
                    field.setAccessible( true );
                    if (StringUtils.isEmpty( idName )){
                        idName = MuyanStringUtils.getTableIdName( field );
                    }else{
                        //字段取到之后,开始将值填充参数列表中
                        Object o = field.get( entity );
                        if (o != null){
                            //继续拼接sql
                            sql.append( tableFieldName ).append( " = ?," );
                            paramsList.add( o );
                        }
                    }
                    if (field.isAnnotationPresent( MuyanId.class )){
                        idValues = field.get( entity );
                    }

                }
                //去掉字段中最后一个逗号,
                sql.deleteCharAt( sql.length() - 1 ).append( " where " + idName +" = ? ");
                paramsList.add( idValues );

            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

既然我们有将实体类转化成数据库字段的类,那我们是不是还要写一个将数据库字段再转化成实体类呢,没错,做了查询,查询结果肯定要返回到前端去,并且根据id查询的,肯定返回实体,如果是不带分页查询的,返回的肯定是带有这个实体泛型的集合,所以我们有必要做这个处理。新建一个InvokeClassAdapter这个类,具体实现,参照如下的代码:

import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanColumn;
import com.muyan.springboot.myspringtransaction.chg.annotation.MuyanId;
import com.muyan.springboot.myspringtransaction.chg.utils.MuyanStringUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.*;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName InvokeClassAdapter
 * @Description 提供数据库查询结果和实体类的转化
 * @Author dym
 * @Date 2019/9/5 17:48
 * @Version 1.0
 **/
@Component
public class InvokeClassAdapter {

    /**
     *@Description  将数据库中的一条记录封装成一个实体类
     *@Author dym
     *@Date 2019/9/3 10:14
     *@Param [resultSet, clazz]
     *@return T
     */
    public static <T> T invokeObject(ResultSet resultSet, Class<T> clazz){
        Map<String,Field> map = new HashMap<>(  );
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent( MuyanColumn.class )) {
                    MuyanColumn annotation = field.getAnnotation( MuyanColumn.class );
                    String annotationColomnName = annotation.colomnName() == null?field.getName(): annotation.colomnName();
                    //将注解名称和属性存放到map中
                    map.put( annotationColomnName,  field);
                }else if (field.isAnnotationPresent( MuyanId.class )){
                    MuyanId annotation = field.getAnnotation( MuyanId.class );
                    map.put( field.getName(), field );
                }
            }
            T obj = clazz.getDeclaredConstructor().newInstance();
            ResultSetMetaData metaData = null;

            metaData = resultSet.getMetaData();

            for (int i = 0;i < metaData.getColumnCount(); i++){
                String columnName = metaData.getColumnName( i + 1 );
                if (map.containsKey( columnName )) {
                    Field field = map.get( columnName );
                    Class<?> type = field.getType();
                    //获取方法名称
                    String setMethod = MuyanStringUtils.getSetMethod( field.getName() );
                    Method method = clazz.getMethod( setMethod, type );
                    Object object = resultSet.getObject( i + 1 );
                    method.invoke( obj, type.cast( object ) );
                }
            }
            return obj;

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }




}

我们的思路继续,一切封装都好了,那么我们需要开始写具体的jdbc了,为了代码的条理性,我们将这个执行jdbc的操作提出来,专门写个类,帮我们做增删改查。这个类叫MuyanJdbcTemplate

import com.muyan.springboot.myspringtransaction.chg.common.InvokeClassAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * @ClassName MuyanJdbcTemplate
 * @Description 具体执行数据库操作
 * @Author dym
 * @Date 2019/8/30 14:48
 * @Version 1.0
 **/
@Component
public class MuyanJdbcTemplate<T> {

    @Autowired
    private MuYanTransctionHolder muYanTransctionHolder;

    @Autowired
    private InvokeClassAdapter invokeClassAdapter;

    /**
     *@Description  执行具体的操作(查询),并且返回实体类
     *@Author dym
     *@Date 2019/9/2 17:50
     *@Param [sql]
     *@return T
     */
    public <T> T executeQueryStatement(StringBuffer sql, Class<T> clazz, List<Object> list) throws IllegalAccessException, InstantiationException {
        T instance = null;
        Connection connection = muYanTransctionHolder.getConnection();
        PreparedStatement preparedStatement = null;
        try{
            preparedStatement = connection.prepareStatement( sql.toString() );
            System.out.println( "查询sql="+sql.toString() );
            if (null != list && list.size() > 0){
                for (int i= 0; i<list.size(); i++){
                    preparedStatement.setObject( i+1, list.get( i ) );
                    System.out.println( "参数"+i+":"+list.get( i ) );
                }
            }

            ResultSet resultSet = preparedStatement.executeQuery();
            if (resultSet.next()){
                instance = (T) invokeClassAdapter.invokeObject( resultSet, clazz );
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return instance;
    }

    /**
     *@Description  查询结果返回集合
     *@Author dym
     *@Date 2019/9/3 17:41
     *@Param [sql, entity]
     *@return java.util.List<T>
     */
    public List<T>  executeQueryStatementByList(StringBuffer sql, Class<T> entity) throws IllegalAccessException, InstantiationException {
        T instance = null;
        List<T> instanceList = new ArrayList<>(  );
        Connection connection = muYanTransctionHolder.getConnection();
        PreparedStatement preparedStatement = null;
        try{
            preparedStatement = connection.prepareStatement( sql.toString() );
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()){
                instance = (T) invokeClassAdapter.invokeObject( resultSet, entity );
                instanceList.add( instance );
            }
            return instanceList;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return Collections.emptyList();
    }






    /**
     *@Description  执行具体的操作(增,改)
     *@Author dym
     *@Date 2019/9/2 17:04
     *@Param [sql, paramsList]
     *@return boolean
     */
    public boolean executeStatement(StringBuffer sql, List<Object> paramsList){
        Connection connection = muYanTransctionHolder.getConnection();
        PreparedStatement preparedStatement = null;
        try {
            preparedStatement = connection.prepareStatement( sql.toString() );
            if (paramsList != null && paramsList.size()  > 0){
                for(int i = 0; i < paramsList.size(); i++){
                    preparedStatement.setObject( i+1, paramsList.get( i ));
                }
            }
            boolean execute = preparedStatement.execute();
            if (execute ){
                return Boolean.TRUE;
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return Boolean.FALSE;
        }finally {
            //因为此处和AOP中使用的connection是同一个,所以此处不能关闭connection,只关闭preparedstatement即可
            try {
                preparedStatement.close();

                //connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return Boolean.FALSE;
    }
}

其中使用了一个MuyanTransctionHolder的类,这个类做什么呢,就是帮我们从数据库中获取连接的,这里有些同学会问不就是获取connection嘛,为什么一行代码还要提出来呢,我提出来的原因在于,因为我手写完ORM之后,我要在下一期手写自己的事务,没错,借助于spring的AOP技术,我们要手写自己的事务。所以我提出来,这本实例中,大家可以暂时不用提出来也行。详细代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Connection;

/**
 * @ClassName MuYanTransctionHolder
 * @Description 
 * @Author dym
 * @Date 2019/8/30 10:20
 * @Version 1.0
 **/
@Component   //交给spring去管理
public class MuYanTransctionHolder {


    @Autowired
    private DataSource dataSource;

    //获取连接
    public Connection getConnection(){
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return connection;

    }
}

到这里我们核心代码都写完了,有了上面的一系列操作,完了我们回过头来继续看我们的BaseMapper这个类,此时应该就简单多了。

import com.muyan.springboot.myspringtransaction.chg.template.MuyanJdbcTemplate;
import org.springframework.beans.factory.annotation.Autowired;

import java.sql.*;
import java.util.*;

/**
 * @ClassName BaseMapper
 * @Description 定义抽象类,用来封装ORM数据映射
 * @Author dym
 * @Date 2019/9/2 9:43
 * @Version 1.0
 **/
public class BaseMapper<T> {

    @Autowired
    private BuildParamToFieldManager<T> buildInsertFieldParams;

    @Autowired
    private MuyanJdbcTemplate<T> muyanJdbcTemplate;


    /**
     *@Description  保存实体
     *@Author dym
     *@Date 2019/9/2 9:52
     *@Param [t]
     *@return boolean
     */
    public boolean add(T t){

        //定义sql
        StringBuffer sql = new StringBuffer(  );

        //参数集
        List<Object> paramsList = new ArrayList<>(  );

        //开始拼接单表新增的sql(Object->Field)
        buildInsertFieldParams.buildInsertFieldParams(t, sql, paramsList);

        //开始执行具体的操作
        boolean b = muyanJdbcTemplate.executeStatement( sql, paramsList );

        return b;

    }


    /**
     *@Description  删除某个实体
     *@Author dym
     *@Date 2019/9/2 9:54
     *@Param [id]
     *@return void
     */
    public boolean delete(Object id){
        //定义sql
        StringBuffer sql = new StringBuffer(  );

        //参数集
        List<Object> paramsList = new ArrayList<>(  );

        //开始拼接sql(Object->Field)
        buildInsertFieldParams.buildDeleteFieldParams(buildInsertFieldParams.getClassInfo(this.getClass()), sql, paramsList, id);

        //执行sql操作
        boolean b = muyanJdbcTemplate.executeStatement( sql, paramsList );

        return b;
    }


    /**
     *@Description  编辑某个实体
     *@Author dym
     *@Date 2019/9/2 9:54
     *@Param [t]
     *@return void
     */
    public boolean update(T t){
        //定义sql
        StringBuffer sql = new StringBuffer(  );

        //参数集
        List<Object> paramsList = new ArrayList<>(  );

        //开始拼接编辑的sql(Object->Field)
        buildInsertFieldParams.buildUpdateFieldParams(t, sql, paramsList);

        //执行sql操作
        boolean b = muyanJdbcTemplate.executeStatement( sql, paramsList );

        return b;
    }


    /**
     *@Description  查询某个实体
     *@Author dym
     *@Date 2019/9/2 9:55
     *@Param [id]
     *@return T
     */
    public T query( Object id){
        //定义sql
        StringBuffer sql = new StringBuffer(  );

        //定义参数列表
        List<Object> paramList = new ArrayList<>(  );

        //获取当前类的实体类
        Class<T> classInfo = buildInsertFieldParams.getClassInfo(this.getClass());

        try {
            //开始拼接参数(Object->Field)
            buildInsertFieldParams.buildSelectFieldParams( classInfo.newInstance(), sql, id, paramList);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        T entity = null;
        try {
            //执行查询具体操作,并且将(sql->Object),最终将查询结果再次反转化为Object返回
            entity = muyanJdbcTemplate.executeQueryStatement( sql , classInfo, paramList );
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

        return entity;
    }

    /**
     *@Description  查询所有数据
     *@Author dym
     *@Date 2019/9/2 9:55
     *@Param []
     *@return java.util.List<T>
     */
    public List<T> queryAll(){
        //定义sql
        StringBuffer sql = new StringBuffer(  );

        //定义参数列表
        List<Object> paramList = new ArrayList<>(  );

        //获取当前类的实体类
        Class<T> classInfo = buildInsertFieldParams.getClassInfo(this.getClass());

        try {
            //开始拼接查询的sql(Object->Field)
            buildInsertFieldParams.buildSelectFieldParams(classInfo.newInstance(), sql, null, paramList);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        List<T> list = null;
        try {
            //执行具体的操作,并且最终将数据库中查询出来的数据封装成实体类的集合返回
            list = muyanJdbcTemplate.executeQueryStatementByList( sql , classInfo);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

        return list;
    }

}

现在看是不是BaseMapper简单明了多了。最后附上application.properties和测试类以及数据库脚本

spring.datasource.url=jdbc:mysql://localhost:3306/student?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver=com.mysql.jdbc.Driver
logging.level.root=debug
server.port=9001
import com.muyan.springboot.myspringtransaction.chg.domain.Student;
import org.junit.Assert;
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.context.junit4.SpringRunner;

import java.util.List;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ChangerStudentServiceTest {

    @Autowired
    private ChangerStudentService changerStudentService;

    @Test
    public void saveData() {
    }

    @Test
    public void saveDataByOrm() {
        Student student = new Student();
        student.setName("muyan456");
        student.setAge(40);
        student.setSex("男");
        boolean b = changerStudentService.saveDataByOrm( student );
        Assert.assertFalse( b );
    }

    @Test
    public void update() {
        Student student = new Student();
        student.setId(35);
        student.setName("muyan1234");
        student.setAge(20);
        student.setSex("男");

        boolean result = changerStudentService.update( student );
        Assert.assertFalse( result );

    }

    @Test
    public void delete() {
        boolean result = changerStudentService.delete( 37 );
        Assert.assertFalse( result );
    }

    @Test
    public void query() {
        Student student = changerStudentService.query( 35 );
        System.out.println( "查询结果为:"+student );
        Assert.assertNotNull( student );
    }

    @Test
    public void queryAll() {
        List<Student> students = changerStudentService.queryAll();
        if (null != students && students.size() > 0){
            for (Student student : students) {
                System.out.println( student );
            }
        }
    }
}
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

忘记粘贴POM.XML了,现在加上去

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.muyan.springboot</groupId>
    <artifactId>myspring-transaction</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>myspring-transaction</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

好了大功告成!因个人技术能力有限,不能做到完美,大佬勿喷,给需要的人做个参考而已,刚开始写博客,积极心最重要,至于源码,后续更新完手写事务之后,一并奉上!

  • 1
    点赞
  • 0
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

一、SBORM 介绍 1、目前只考虑支持 mysql; 2、基于spring jdbc的上层封装,底层jdbc操作基于JdbcTemplate,对于使用spring jdbc的人会有一点价值,比较简洁的封装可以节省很多重复劳动,具体节省多少可以看看example; 3、实现一套简单的ORM(直接使用spring rowmapper,insert自己实现),可以基于对象进行crud和相对复杂(感觉比hibernate强大一点)的sql操作; 4、基于对象指定查询的字段,大部分时候可以忘掉表结构进行业务开发; 5、支持简单的数据库路由,读写分离(半自动,需要指定取writer还是reader,默认规则reader采用随机的方式,当然也可以手动指定); 6、支持简单的分表,主要是针对一定规则的分表,比如百分表、千分表,也可以自己指定分表后缀; 7、简单的单表查询(比如所有条件是and或者or结构),基本实现0sql代码编写(类似HibernateTemplate selectByExample、findByCriteria、find等方法); 8、简单的单表排序支持,支持多个排序条件组合; 9、对于复杂的sql查询,提供获取jdbctemplate实例进行操作,类似spring jdbc的常规用法; 10、提供Entity代码生成接口,Entity并非简单的pojo(尽可能不要去修改此类),引入字段常量类,方便查询的时候指定选择字段,从而更好实现查询条件的封装; 二、为什么写SBORM? 1、hibernate:过于臃肿,使用不够灵活,优化难(其实主要是因为很少用),HQL感觉就是个渣,在 mysql几乎一统天下的背景下,跨数据库级别的兼容吃力不讨好。Hibernate的对象化关联处理确实挺强大,但是使用起来坑太多,有多少人敢在项目 中大范围使用真不知道,屠龙刀不是人人都提的起啊。 2、mybatis:轻量级,基于xml的模式感觉不利于封装,代码量不小,基于xml维护也麻烦(个人观点, 现在注解模式貌似也挺不错),感觉mybatis更适合存在dba角色的年代,可以远离代码进行sql调优,复杂的查询拼装起来也更加优雅(java基本 就是if else ...),但是对于查询业务简单但是数据库集群环境的场景有点憋屈(其实对mybatis使用也不多,瞎评论^_^)。 3、spring jdbc:小巧,灵活,足够优秀,个人比较喜欢使用,但是代码量偏大,原生的接口重复劳动量大,比如insert、mapper之类的; SBORM只是针对spring jdbc的一些不方便的地方,做了一些封装,更加简化日常的开发工作,基于spring jdbc的RowMapper自动实现对象映射,也勉强算的上叫ORM,只是大部分功能已经由spring jdbc实现了。 平时不太喜欢使用hibernate和mybatis,主要是使用spring jdbc,写这个东西的出发点主要是平时使用spring jdbc觉 得比较麻烦,重复性的代码偏多,一方面通过自动mapper降低返回结果处理工作量,另一方面参考hibernate对象化查询条件的模式,写了一个 QueryBudiler,使得更多简单的单表查询可以通过对象组织查询、更改逻辑,避免过多去写相似性的SQL语句,减少DAO接口量。 三、一些亮点 1、Entity的设计:很多人看了也许会说,这个不是POJO,不是纯粹的Java Bean,显得很另类。但是有多人在开发过程中(特别是在写sql的时候),经常要去看看表结构设计?还有多少次因为改了表某个字段,还得遍历去查找哪些 sql使用了这个字段?多少次看到在代码中直接传入字段名作为查询参数感到别扭?如果将表结构字段都用java对象去描述,能够解决这些问题,就不必要在 乎是不是POJO了,后面看example的时候应该能体会这么做的一些好处,至少我觉得是挺方便的,将大部分查询脱离表结构设计。 2、简单的数据库路由:如果分库结构不是太复杂(比如简单的读写分离、或者多个库集成),BaseDao可以自 动进行路由(比如读写分离,根据业务模式指定读、写库),如果非默认的路由规则,也可以通过手动设置的模式,进行数据库路由。数据库路由直接由 Entity指定,所有的路由都是根据Entity识别,也就是说查询也是围绕Entity展开的,避免类似使用spring jdbc的时候,各种 template实例跳来跳去,硬编码引入,写一个业务还得看看到底该用哪个template,尤其是多个数据库共用一个template实例的时候。 3、QueryBuilder:单表查询基本上都可以实现零Sql(除非查询条件特别复杂的),更新、删除等操作也可以通过QueryBuilder进行批量处理,不局限于根据主键来处理。 4、分表操作的支持:对于分表操作和常规的使用没有区别,只是指定分表规则,mybatis好像也可以通过制定参数实现分表处理,没搞清楚hibernate对这个是怎么处理的(hibernate好像是bean和表一对一绑定的)? 标签:sborm
©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值