Java8的Lambda表达式确实是一个很好的特性。可是在哪些场合下使用。事实上还是须要细致考虑的。我们当然不能为了使用而使用,而是须要找到切实实用的场合。在JDBC编程中,比如查询语句,首先须要进行查询參数绑定,其次是处理返回的结果集,这两步操作是每一个查询都不同的,而获取JDBC连接,准备PreparedStatement,以及释放资源则都是全然同样的,这就是一个Lambda表达式应用的绝佳场景。
在讨论详细的实现细节之前,想先讨论一下JDBC的问题。
眼下相信90%以上的Java程序猿都不会再用JDBC了,基本都採用某种OR映射机制。如Hibernate、Persistent API等,採用所谓OO的方式来操作数据库。
可是实际上这样的方式在理论上非常好,在实际应用中是有非常大问题的,尤其在眼下大数据的情况下。将数据和程序绑在一起是非常不可取的,同一时候採用OR映射之后,抽象出的数据操作在功能和性能方面,都全然不能与SQL相比,因此直接使用JDBC还是值得考虑的。如今的语言之争,基本是该语言下最流行的框架之争,Java语言下基本就是SSH和JBOSS,因为这两个异常流行和笨重的框架。使Java基本淡出了互联网应用开发领域。事实上结合Java的NIO 2以及近期的Lambda表达式、Stream API的新特性。全然有可能写比Node.js更优秀的框架,仅仅可惜java开发社区在自己的思维惯性下。非常难使这样的可能变为现实。
好了,言归正传。我们首先如果我们须要查询数据库中的t_user表,当中有user_id, user_name, nick_name, birthday等字段,我们须要构造一个SQL查询。找出user_id小于10000的记录。
首先我们先定义值对象:
public class UserInfo {
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public Calendar getBirthday() {
return birthday;
}
public void setBirthday(Calendar birthday) {
this.birthday = birthday;
}
private long userId = 0;
private String userName = null;
private String nickName = null;
private Calendar birthday = null;
}
其次我们定义获取和释放JDBC连接的函数:
private Connection getConnection() {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/WkyDb?
user=wky&password=wky123&" + "useUnicode=true&characterEncoding=utf-8&" + "autoReconnect=true&failOverReadOnly=false"); } catch (Exception e) { System.exit(0); } return conn; } private void closeConnection(Connection conn) { try { if (conn != null && !conn.isClosed()) { conn.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
以下我们来定义參数绑定函数式接口:@FunctionalInterface
public interface FParamBinder<T> {
public void bindParams(PreparedStatement stmt, T param) throws SQLException;
}
接下来我们定义接收返回结果的函数式接口:
@FunctionalInterface
public interface FResetSetter<T> {
public List<T> getResultSet(ResultSet rst) throws SQLException;
}
以下是SQL查询的详细实现函数:
public <T> List<T> executeQuery(String sql, FParamBinder<T> binder, T cond, FResetSetter<T> setter) {
Connection conn = getConnection();
PreparedStatement stmt = null;
ResultSet rst = null;
List<UserInfo> items = new ArrayList<UserInfo>();
UserInfo item = null;
List<T> recs = null;
try {
stmt = conn.prepareStatement(sql);
binder.bindParams(stmt, cond); // 调用參数绑定函数式接口
rst = stmt.executeQuery();
recs = setter.getResultSet(rst); // 获取返回结果集
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (rst != null) {
rst.close();
}
if (stmt != null) {
stmt.close();
}
} catch (Exception ex) {}
}
closeConnection(conn);
return recs;
}
这个函数实现与普通的JDBC编程中的查询不同。參数绑定和接收结果集这两部分都是通过传入的函数式接口变量来实现,因此代码显得很简洁。
以下是其它函数调用SQL查询时对上面函数的调用,例如以下所看到的:
public void testIt() {
String sql = "select user_id, user_name, nick_name, birthday from t_user where user_id<?";
FParamBinder<UserInfo> binder = (PreparedStatement stmt, UserInfo param) -> {
stmt.setLong(1, param.getUserId());
};
UserInfo cond = new UserInfo();
cond.setUserId(10000000L);
FResetSetter<UserInfo> setter = (ResultSet rst) -> {
List<UserInfo> items = new ArrayList<UserInfo>();
UserInfo item = null;
while (rst.next()) {
item = new UserInfo();
item.setUserId(rst.getLong(1));
item.setUserName(rst.getString(2));
items.add(item);
}
return items;
};
List<UserInfo> recs = executeQuery(sql, binder, cond, setter);
System.out.println("size=" + recs.size() + "!");
for (UserInfo u : recs) {
System.out.println("U:" + u.getUserName() + "-" + u.getUserId() + "!");
}
}
如上所述。调用者须要指定查询SQL语句,定义參数绑定函数式接口变量,结果集接收函数式接口变量,然后调用上面的executeQuery方法,就能够获取到结果集。然后就能够进行下一步的操作了。