锁定老帖子 主题:我使用DSL编写SQL的一个Java实现
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-03-21
1.导读
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(1, new 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 你遇到过这样的问题么?
2.3 这个问题可以怎么解决呢?
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'
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={5, 4, 1, 3, 9, 8, 7, 2, 0}; 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); } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-11-25
哇,挺好的。
我正想去写一个,简单的拼写sql太纠结了。 thx. |
|
返回顶楼 | |
发表时间:2010-11-26
这给我很大的启示,原来一直是拼sql。很牛,楼主加油啊....
|
|
返回顶楼 | |
发表时间:2010-11-26
我刚刚看过了
mybatis |
|
返回顶楼 | |
发表时间:2010-11-27
这个思路很好。对我很有帮助
|
|
返回顶楼 | |
发表时间:2010-11-28
最后修改:2010-11-28
哈哈,很感谢隔了一段时间还有人回复。我循着这个思路做了个小工具,现在还没有正式的release,正在努力写文档和example。
项目主页:http://code.google.com/p/likesql/ 我的另一篇文章,讨论PreparedStatement的缺点:http://www.iteye.com/topic/620906 |
|
返回顶楼 | |
发表时间:2010-11-28
看起来挺不错的
|
|
返回顶楼 | |
发表时间:2010-11-30
最后修改:2010-11-30
以前大部分项目都是SQL拼接的
方法很不错,但是我又一个问题和疑惑 如果我插入的数据很多 那么我的value()不是很多吗?一层一层的!这个也是一个问题!!! 例如下面: InsertSQL sql=new InsertSQL(); sql.insertInto("students") value("id", new Integer(id)) .value("name", name) .value("gender",gender) ....... ;//<-----很多次的value System.out.println(sql.toPreparedString()); System.out.println(sql.toString()); |
|
返回顶楼 | |
浏览 4131 次