根据实体生成sql语句

五一前无意间看到了一篇Java反射获取注解的文章,突发奇想,自己也尝试写个根据实体来生成sql语句的短文吧,也算是加深下自己对Java反射知识的理解。

话不多说,因为笔者用多了通用Mapper+MyBaits的敏捷开发。所以自己写的demo或多或少有通用Mapper的一些影子。

整体结构:

根据实体生成sql语句_第1张图片根据实体生成sql语句_第2张图片

启动类:

public class Test {

    public static void main(String [] args) throws IllegalAccessException, InstantiationException {
        //todo

        EmpMapper empMapper = new EmpMapper(Emp.class);
        Emp emp = new Emp();
        emp.setId(1);
        emp.setName("Tom");
        emp.setAge("18岁");
        emp.setSex("男");
        empMapper.insert(emp);
    }
}

这里我就不执行sql了,只是把生成的sql语句打印出来就ok。

控制台打印结果:

INSERT INTO tb_emp ( t_sex, t_name, t_id, t_age) VALUES ( '男', 'Tom', 1, '18岁')

代码如下:

/**
 * @Author: [email protected]
 * @Description: 模拟Table注解
 * @Params:
 * @Return: 
 * @Throws: 
 * @Date: Created in 2020/4/30 14:59
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {

    String value();
}

Column、Id注解和Table注解类似。

public class EmpMapper extends MyMapper {

    public EmpMapper() {
    }

    public EmpMapper(Class clazz) {
        super(clazz);
    }
}

Emp实体的Mapper接口。

@Table("tb_emp")
public class Emp {

    @Column("t_id")
    private Integer id;

    @Column("t_name")
    private String name;

    @Column("t_age")
    private String age;

    @Column("t_sex")
    private String sex;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

Emp实体类。

public class MyMapper implements InsertMapper{

    private Class clazz;

    private InsertMapper mapper;

    public MyMapper() {
    }

    public MyMapper(Class clazz) {
        this.clazz = clazz;
        mapper = new InsertMapperImpl(clazz);
    }



    @Override
    public int insert(T t) {
        return mapper.insert(t);
    }

    @Override
    public int insertSelective(T t) {
        return mapper.insertSelective(t);
    }
}

所有实体的Mapper都继承统一的MyMapper类,在MyMapper里调用相应的生成sql的方法。

public interface InsertMapper {

    /**
     * @Author: xiake
     * @Description: 新增一条数据
     * @Params:
     * @Return:
     * @Throws:
     * @Date: Created in 2020/4/30 15:05
     */
    int insert(T t);

    /**
     * @Author: xiake
     * @Description: 新增一条数据,为null的字段不插入
     * @Params:
     * @Return:
     * @Throws:
     * @Date: Created in 2020/4/30 15:07
     */
    int insertSelective(T t);

}

这里我对新增、修改、删除和查询Mapper生成的相应sql语句都独立做接口,让它们统一实现。

public class InsertMapperImpl implements InsertMapper {

    private Class clazz;

    private TableHelper tableHelper;

    public InsertMapperImpl() {
    }

    public InsertMapperImpl(Class clazz) {
        this.clazz = clazz;
        this.tableHelper = new TableHelper(clazz);
    }

    @Override
    public int insert(T t) {
        StringBuilder sb = new StringBuilder();
        //拼接INSERT SQL语句的前缀:INSERT INTO
        sb.append(appendPrefixSql());
        //拼接表里的字段名,这里根据实体里属性对应的@Column注解来实现
        sb.append(appendColumnNamesSql());
        //拼接 VALUES 关键字
        sb.append(appendValuesSql());
        //拼接实体里对应属性的值,String类型需要加''修饰
        sb.append(appendColumnValueSql(t));
        //这里不做执行sql操作,只是把生成的sql语句打印出来
        System.out.println(sb.toString());
        //返回的sql执行成功条数,肯定为1啦
        return 1;
    }

    @Override
    public int insertSelective(T t) {
        return 0;
    }

    /**
     * @Author: [email protected]
     * @Description: INSERT sql 语句的前缀
     * @Params:
     * @Return:
     * @Throws:
     * @Date: Created in 2020/4/30 15:46
     */
    private String appendPrefixSql(){
        StringBuilder sb = new StringBuilder();
        sb.append("INSERT INTO ");
        sb.append(tableHelper.getTable());
        sb.append(" ");
        return sb.toString();
    }

    /**
     * @Author: [email protected]
     * @Description: INSERT sql 语句的插入列名 (column1,column2,column3,column4...)
     * @Params:
     * @Return:
     * @Throws:
     * @Date: Created in 2020/4/30 16:02
     */
    private String appendColumnNamesSql(){
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        Set fieldNames = tableHelper.getFieldNames();
        fieldNames.stream()
                .forEach((fieldName) ->
                    sb.append(" ").append(tableHelper.getField(fieldName).getDeclaredAnnotation(SqlHelper.COLUMN_CLASS).value()).append(",")
                );
        sb.replace(sb.length() - 1, sb.length(), ")");
        return sb.toString();
    }

    private String appendColumnValueSql(T t){
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        Set filedNames = ColumnHelper.getFieldNames(clazz);
        filedNames.stream()
                .forEach((filedName) -> {
                    sb.append(" ");
                    if ("java.lang.String".equals(tableHelper.getField(filedName).getType().getName())) {
                        sb.append("'")
                                .append(getFiledValue(t, filedName))
                                .append("'")
                                .append(",");
                    } else {
                        sb.append(getFiledValue(t, filedName))
                                .append(",");
                    }
                });
        sb.replace(sb.length() - 1, sb.length(), ")");
        return sb.toString();
    }

    /**
     * @Author: [email protected]
     * @Description: 拼接VALUES字符串
     * @Params:
     * @Return:
     * @Throws:
     * @Date: Created in 2020/4/30 16:05
     */
    private String appendValuesSql(){
        return " VALUES ";
    }

    private Object getFiledValue(T t, String filedName) {
        String getterMethodName = ColumnHelper.getterMethodName(filedName);
        Object obj = null;
        try {
            Method method = clazz.getDeclaredMethod(getterMethodName);
            obj = method.invoke(t);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

}

下面还有部分工具类也贴出来:

public class SqlHelper {

    public static final Class ID_CLASS = Id.class;

    public static final Class TABLE_CLASS = Table.class;

    public static final Class COLUMN_CLASS = Column.class;
} 
  
public class TableHelper {

    private Class clazz;

    /**
     * @Id注释的属性值
     */
    private Set pKFieldNames;

    /**
     * @Column注释的列
     */
    private Set fieldNames;

    /**
     * 是否被@Table注解注释
     */
    private boolean isTable;

    /**
     * Table名称
     */
    private String tableName;

    public TableHelper(Class clazz) {
        this.clazz = clazz;
        init();
    }

    public void init(){

        isTable = clazz.isAnnotationPresent(SqlHelper.TABLE_CLASS);

        Table table = clazz.getDeclaredAnnotation(SqlHelper.TABLE_CLASS);
        tableName = table != null ? table.value() : null;

        this.fieldNames = ColumnHelper.getFieldNames(clazz);

        this.pKFieldNames = ColumnHelper.getPKFieldNames(clazz);

    }

    public boolean isTable(){
        return isTable;
    }

    public String getTable() {
        if (!isTable) {
            throw new RuntimeException("实体没有被@Table注释修饰");
        }
        return tableName;
    }

    public Set getFieldNames() {
        return fieldNames;
    }

    public Field getField(String fieldName){
        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Set getpKFieldNames() {
        return pKFieldNames;
    }

}
public class ColumnHelper {

    /**
     * @Author: xiake
     * @Description: 获得该clazz类的所有带@Column注解的属性名集合
     * @Params:
     * @Return: 
     * @Throws: 
     * @Date: Created in 2020/4/30 15:36
     */
    public static Set getFieldNames(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        return Arrays.stream(fields)
                .filter(filed -> filed.isAnnotationPresent(SqlHelper.COLUMN_CLASS))
                .map(Field::getName)
                .collect(Collectors.toSet());
    }

    /**
     * @Author: xiake
     * @Description: @Id修饰的属性名集合
     * @Params:
     * @Return: 
     * @Throws: 
     * @Date: Created in 2020/5/6 10:45
     */
    public static Set getPKFieldNames(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        return Arrays.stream(fields)
                .filter(filed -> filed.isAnnotationPresent(SqlHelper.ID_CLASS))
                .map(Field::getName)
                .collect(Collectors.toSet());
    }

    public static String setterMethodName(String fieldName){
        return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    public static String getterMethodName(String fieldName){
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

}

大概生成sql就是这样子,代码写的有点简陋。。。。。。

你可能感兴趣的:(学习之旅)