<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Heis的Blog

    保持簡單,保持愚蠢
    隨筆 - 29, 文章 - 1, 評論 - 122, 引用 - 0
    數據加載中……

    我使用DSL編寫SQL的一個Java實現

    1.導讀

    • 什么是DSL?領域特定語言(Domain Specific language)通常被定義為一種特別針對某類特殊問題的計算機語言,它不打算解決其領域外的問題。了解更多

    2.你使用JDBC來 存取 數據時,怎么處理你的SQL

    2.1 對于一個固定條件的查詢,我們會使用PreparedStatement來實現。就像下面這個例子,只需要DateOfBirth一個固定條件來查詢。

    PreparedStatement statement = null;

        
    try {

            Connection connection 
    = getConnection();

            statement 
    = connection.prepareStatement(

                    
    "SELECT Name" +

                    
    " FROM Students" +

                    
    " WHERE DateOfBirth < ?");

            statement.setDate(
    1new java.sql.Date(new java.util.Date().getTime()));

             ResultSet rs 
    = statement.executeQuery();

            
    while (rs.next()) {

                System.out.print(rs.getString(
    1));

            }

        } 
    catch (SQLException e) {

            e.printStackTrace();

        }

    2.2 你遇到過這樣的問題么?

    • 你使用JDBC來實現數據存取,如果你要實現一個復雜條件的查詢,而且條件數目還不一定,這時候就很難使用PreparedStatement來 解決了,因為你的SQL模板不是固定的。就像上面的這個例子,如果用戶可能要使用DateOfBirth或者Name作為條件查詢,或者還有更多的條件。

    2.3 這個問題可以怎么解決呢?

    你當然可以使用簡單的字符串拼接,根據不同的條件拼接成不同的SQL。就像以下代碼
    int id = 0;
            String name 
    = "Heis";
            String gender 
    = "male";
    String sql 
    = "select Name from Students where id=" + id;
            
    if (name != null) {
                sql 
    += " and name='" + name + "";
            }
            
    if (gender != null) {
                sql 
    += " and gender='" + gender + "";
            }
            System.out.println(sql);

      輸出:
    select Name from Students where id=0 and name='Heis'  and gender='male'

    這樣處理的缺點是很明顯的。首先,敏感字符沒有過濾,容易被注入攻擊 ;其次,代碼不容易讀;第三,出于debug的需要,我希望可以保留SQL模板作日志記錄,而不是完整的SQL,就是希望用問號?代替真實的數據。

    3. 我的解決方案

    我同樣在項目中遇到這樣的問題,所以借助DSL的思想對SQL做了一些封裝。把SQL實現為java版的DSL,這樣不但不會失去SQL的簡單易懂的特性,而且本來SQL就是一門DSL,實現起來不會太困難。

    我實現的QuerySQL:

    int id = 0;
            String name 
    = "Heis";
            String gender 
    = "male";
            QuerySQL sql 
    = new QuerySQL();
            
            sql.select(
    "name")
               .from(
    "Students")
               .where(
    "id=?"new Integer(id));
            
            
    if (name != null) {
                sql.and(
    "name='?'",name);
            }
            
            
    if (gender != null) {
                sql.and(
    "gender='?'",gender);
            }
            
            System.out.println(sql.toPreparedString());
            System.out.println(sql.toString());

    輸出:

    select name from Students where id=and name='?' and gender='?'
    select name from Students where id=0 and name='Heis' and gender='male'


    4. QuerySQL是怎么實現的

    其實實現的原理也很簡單,就是在QuerySQL的內部準備兩個StringBuffer,一個用來拼接SQL模板,另一個是拼接SQL;而對于API的設計,只要在完成拼接后,返回實例本身即可。

    QuerySQL實現的片段:

    public class QuerySQL extends SQL {
    public QuerySQL() {
            buffer 
    = new StringBuffer(100);
            preBuffer 
    = new StringBuffer(90);
        }

    public QuerySQL select(String value) {
            buffer.append(SELECT);
            preBuffer.append(SELECT);
            append(value);
            
    return this;
        }

    public QuerySQL and(String pattern, Object value) {
            String str 
    = format(pattern, value);
            buffer.append(WS).append(AND).append(WS).append(str);
            preBuffer.append(WS).append(AND).append(WS).append(pattern);
            
    return this;
        }
    //format 會過濾掉value的敏感字符
    protected String format(String pattern, Object value) {
            
    if (value instanceof String) {
                String val 
    = (String) value;
                val 
    = SymbolUtils.filterSensitiveSQLSymbol(val);
                
    return StringUtils.replaceFirst(pattern, CHAR_FOR_REPLACE, val);
            } 
    else if (value instanceof java.sql.Date) {
                Date date 
    = DateUtils.convertToDate((java.sql.Date) value);
                
    return StringUtils.replaceFirst(pattern, CHAR_FOR_REPLACE,
                        DateUtils.formatDate(date));
            } 
    else if(value instanceof Date){
                
    return StringUtils.replaceFirst(pattern, CHAR_FOR_REPLACE,
                        DateUtils.formatDate(value));
            }
    else {
                
    return StringUtils.replaceFirst(pattern, CHAR_FOR_REPLACE, value
                        .toString());
            }
        }


    }


    5. 關于Insert語句

    對于Insert語句,如果插入的數據非常多,涉及很多個column,insert語句就顯得不是那么直觀了。你甚至要數著第幾個column是什么類型,要插入相應的數據類型。

    statement = connection.prepareStatement("insert into students(id,name,gender) values(?,?,?,?)");
                statement.setInt(
    1, id);
                statement.setString(
    2, value2);
                statement.setString(
    3, value3);
                            
    statement.setString(n, valueN);

    經過我封裝的InsertSQL類

    InsertSQL sql=new InsertSQL();
            sql.insertInto(
    "students")
               .value(
    "id"new Integer(id))
               .value(
    "name", name)
               .value(
    "gender",gender);
            
            System.out.println(sql.toPreparedString());
            System.out.println(sql.toString());


    輸出:

    insert into students (id,name,gender) values(?,?,?)
    insert into students (id,name,gender) values('0','Heis','male')


    6. 后記

    如果你對于這個實現感興趣,可以下載源代碼來看。但是我不推薦你在項目中使用,因為這個實現并不完整,很多地方還欠考慮,而且我還在不斷地修改。寫這篇文章的目的是希望作為一個導讀,讓更多人可以來探討DSL,多交流java實現的DSL。


    點擊下載源代碼



    7. 延伸閱讀


    7.1 JEQUEL(Java Embedded QUEry Language)

    描述:比較完整的一個開源的SQL/DSL實現

    官方主頁:http://www.jequel.de/index.php

    官方示例:

    public void testSimpleSql() {
            
    final SqlString sql =
                    select(ARTICLE.OID)
                            .from(ARTICLE, ARTICLE_COLOR)
                            .where(ARTICLE.OID.eq(ARTICLE_COLOR.ARTICLE_OID)
                                    .and(ARTICLE.ARTICLE_NO.is_not(NULL)));

            assertEquals(
    "select ARTICLE.OID" +
                         
    " from ARTICLE, ARTICLE_COLOR" +
                         
    " where ARTICLE.OID = ARTICLE_COLOR.ARTICLE_OID" +
                         
    " and ARTICLE.ARTICLE_NO is not NULL", sql.toString());
        }

    7.2 Quaere

    描述:一個類似LINQ的java實現

    官方主頁:http://quaere.codehaus.org/

    官方示例:

    Integer[] numbers={541398720};
    Iterable
    <Integer> lowNumbers=
            from(
    "n").in(numbers).
            where(lt(
    "n",5).
            select(
    "n");

    System.out.println(
    "All numbers that are less than five:")
    for (Integer n: lowNumbers) {
        System.out.println(n);
    }

    7.3 EoD SQL

    描述:利用Annotation來聲明SQL

    官方主頁:https://eodsql.dev.java.net/

    官方示例:

     public interface UserQuery extends BaseQuery {
         @Select(
    "SELECT * FROM users WHERE id = ?1")
         
    public User getUserById(long id);
     
         @Select(
    "SELECT * FROM users")
         
    public DataSet<User> getAllUsers();
     
         @Update(
    "UPDATE users SET user_name = ?{1.userName}, email_address = ?{1.emailAddress} " +
         
    "dob = ?{1.dob} WHERE id = ?{1.id}")
         
    public void updateUser(User user);
     
         @Update(sql 
    = "INSERT INTO users (user_name, email_address, dob) VALUES " +
         
    "(?{1.userName}, ?{1.emailAddress}, ?{1.dob})",
         keys 
    = GeneratedKeys.RETURNED_KEYS_FIRST_COLUMN)
         
    public User insertUser(User user);
     
     }









    程序員的一生其實可短暫了,這電腦一開一關,一天過去了,嚎;電腦一開不關,那就成服務器了,嚎……

    posted on 2010-03-21 23:41 Heis 閱讀(4949) 評論(2)  編輯  收藏 所屬分類: 雜七雜八

    評論

    # re: 我使用DSL編寫SQL的一個Java實現  回復  更多評論   

    創意不錯
    2010-03-22 17:48 | 隔葉黃鶯

    # re: 我使用DSL編寫SQL的一個Java實現  回復  更多評論   

    這個正是我想找的啦。呵呵
    2010-03-23 15:16 | fantasy
    主站蜘蛛池模板: 99在线免费观看视频| 免费视频成人片在线观看| 国产精品成人无码免费| 最近中文字幕完整免费视频ww| www.免费在线观看| 亚洲国产人成在线观看69网站| 亚洲国产成人无码AV在线影院| 最新免费jlzzjlzz在线播放| 国产L精品国产亚洲区久久| 精品女同一区二区三区免费播放| 亚洲成A人片77777国产| 亚洲精品亚洲人成在线麻豆| 高清免费久久午夜精品| 亚洲国产精品尤物yw在线| 中文字幕版免费电影网站| 日本无吗免费一二区| 亚洲av无码专区青青草原| 亚洲精品tv久久久久| 亚洲成a人片在线观看精品| 成人五级毛片免费播放| 亚洲视频一区二区三区| 免费毛片a在线观看67194| 久久精品国产亚洲av麻豆图片 | 免费无码肉片在线观看| 久久久久亚洲爆乳少妇无| 91视频免费观看| 国产精品亚洲二区在线观看| 大妹子影视剧在线观看全集免费| 久久精品亚洲一区二区 | 午夜电影免费观看| 一出一进一爽一粗一大视频免费的| 欧美在线看片A免费观看| 在线观看亚洲AV日韩A∨| a级毛片无码免费真人| 亚洲AV无码一区二区一二区| 亚洲综合无码AV一区二区 | 免费毛片毛片网址| 亚洲AV区无码字幕中文色 | 国产午夜精品理论片免费观看| 亚洲天堂中文字幕在线观看| 国产成人精品123区免费视频|