手把手教你寫一個java的orm(四)


開始准備生成sql

在上一篇里,我們已經取到了我們在生成sql語句中所需要的信息,這一篇里我們開始根據class來生成我們需要的sql。在這之前我們先確認幾件事情

  1. sql里的參數我們使用占位符的形式。

    這里用的是jdbc中的PreparedStatement,sql中的參數使用“”的形式。

    大致上是這樣的:

    Connection connection = dataSource.getConnection();
    PreparedStatement preparedStatement = connection.prepareStatement("select * from `user` where `status` = ? ;");
    preparedStatement.setObject(1, 0);
    ResultSet resultSet = preparedStatement.executeQuery();
    

    但是這樣的話我們每次執行都需要手寫這些執行sql的繁瑣的代碼,我在這里選擇使用spring-jdbc中的JdbcTemplte。這樣我就只需要生成sql,然后使用JdbcTemplte里的方法來執行sql就好了。

  2. 我們只生成單表的增刪改查,不涉及復雜sql。

  3. 不貼出完整的代碼,以說明思路為主。

    畢竟這個是已經寫好的代碼,地址在:https://github.com/hjx601496320/JdbcPlus 。所有代碼可以在這里找到。

分析sql

我們主要解決的是增刪該查的問題,所以我們先寫如何生成一個新增的sql。

我么先觀察一下sql一般來說都有什么構成。現在先放一個例子出來:

  1. insert

    INSERT INTO user (name, id, create_date, age, mark, status)
    VALUES (?, ?, ?, ?, ?, ?);
    
  2. delete

    DELETE
    FROM user
    WHERE id = ? 
    
  3. update

    UPDATE user
    SET name        = ?,
        id          = ?,
        create_date = ?,
        age         = ?,
        status      = ?
    WHERE id = ? 
    
  4. select

    SELECT name, id, create_date, age, mark, status
    FROM user
    WHERE id = ?
    

通過觀察上面的sql,可以發現其中有一些共性:

  1. 都有表的名稱。
  2. 基本上都包含表中的字段名稱。
  3. 還有參數。
  4. 以上都是廢話 😉

接下來,就可以按照每種類型的sql來創建sql了。

操作對象

一下所有的對象都是這個User.java


import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;


@Table(name = "user")
public class User {

    @Column(name = "name")
    private String name;

    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "age")
    private int age;

    @Column(name = "mark")
    private String mark;

    @Column(name = "create_date")
    private Date createDate;

    @Column(name = "status")
    private int status;

//   getter setter toString
}

先寫點工具代碼

主要用來操作字符串


import java.util.Collection;
import java.util.Iterator;

/**
 * @author hjx
 */
public class StringUtils {

    public static final String SPACE = " ";

    public static final String BLANK = "";

    public static final String COMMA = ", ";


    /**
     * 重復字符串
     *
     * @param str
     * @param number
     * @return
     */
    public static String[] repeat(String str, int number) {
        Assert.notNull(str);
        String[] strings = new String[number];
        for (int i = 0; i < number; i++) {
            strings[i] = str;
        }
        return strings;
    }

    /**
     * 組合字符串
     *
     * @param strings
     * @return
     */
    public static String append(final Object... strings) {
        StringBuilder builder = new StringBuilder();
        for (Object s1 : strings) {
            if (s1 == null) {
                continue;
            }
            builder.append(s1.toString());
        }
        return builder.toString();
    }

    /**
     * 組合字符串
     *
     * @param collection
     * @param separator
     * @return
     */
    public static String join(Collection collection, String separator) {
        StringBuffer var2 = new StringBuffer();
        for (Iterator var3 = collection.iterator(); var3.hasNext(); var2.append((String) var3.next())) {
            if (var2.length() != 0) {
                var2.append(separator);
            }
        }
        return var2.toString();
    }
}

用來從對象中取值的,使用反射。

/**
 * 取值
 *
 * @param target 要從哪一個對象中取值
 * @param field  要取這個對象的那個屬性的值
 * @return
 */
public static Object getValue(Object target, Field field) {
    //忽略掉private
    field.setAccessible(true);
    try {
        return field.get(target);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

​ 用來給對象設置值的,還是反射。

/**
 * 設置值
 *
 * @param target 要從哪一個對象中取值
 * @param field  要取這個對象的那個屬性的值
 * @param value  要設置的值
 * @return
 */
public static boolean setValue(Object target, Field field, Object value) {
    field.setAccessible(true);
    try {
        field.set(target, value);
        return true;
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return false;
}

下面就可以開始創建各種sql了~~~

生成sql:insert

思路

新增的sql還是比較好實現的,我們需要的大致就是:

  1. 構建一個對象 User。
  2. 調用新增的方法,將User作為參數傳入方法。
  3. 通過上一篇的解析結果,拿到所有的字段名稱,與要保存的值。生成sql。
  4. 通過JdbcTemplate執行sql,插入數據庫。

實現

首先我們要根據User.java拿到所有的表的字段個名稱,和對應的值。就是上一篇寫到的:EntityTableRowMapper

  1. 拿到字段和class屬性的值

    Map<String, Field> columnFieldMapper = entityTableRowMapper.getColumnFieldMapper();
    insertColumns = new ArrayList(columnFieldMapper.size());
    for (Map.Entry<String, Field> stringFieldEntry : columnFieldMapper.entrySet()) {
        Field field = stringFieldEntry.getValue();
        Object value = EntityUtils.getValue(entity, field);
        if (value == null) {
            continue;
        }
        insertColumns.add(stringFieldEntry.getKey());
        insertColumnValues.add(value);
    }
    

    這里有兩個變量:

    insertColumns:sql中的字段名。

    insertColumnValues:sql中的字段對應的值。

  2. 生成插入的sql:

    StringBuilder builder = new StringBuilder();
    int size = insertColumns.size();
    builder.append("INSERT INTO ").append(getTableName()).append(StringUtils.SPACE);
    builder.append(StringUtils.append("( ", StringUtils.join(insertColumns, ", "), " ) "));
    builder.append("VALUES ");
    for (int i = 0; i < insertCount; i++) {
        builder.append("( ");
        String[] repeat = StringUtils.repeat("?", size);
        builder.append(StringUtils.join(Arrays.asList(repeat), ", "));
        builder.append(" )");
        if (i != insertCount - 1) {
            builder.append(StringUtils.COMMA);
        }
    }
    builder.append(";");
    
  3. 生成的結果:

    //user
    User user = new User();
    user.setId(10);
    user.setCreateDate(new Date());
    user.setAge(20);
    user.setMark("ceshi");
    user.setName("heiheihei");
    //sql
    INSERT INTO user ( name, id, create_date, age, mark, status ) VALUES ( ?, ?, ?, ?, ?, ? );
    //value
    [heiheihei, 10, Tue Jan 22 16:33:00 CST 2019, 20, ceshi, 0]
    
  4. 現在可以拿着生成的sql和值去執行啦~

    jdbcTemplate.update(sql, insertColumnValues.toArray());
    
結束啦,剩下的下一篇寫~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM