三层架构的意义
还记得我当初学 Java 操作数据库的示例代码时要做的步骤:
- 连接数据库
- 编译 sql
- 执行 sql,获取结果集
- 遍历结果集,封装对象
- 得到的结果(或集合)返回出去
- 在 Servlet 中调用写好的函数,将结果转换成 JSON 字符串返回给前端
public class JdbcMySql {
private String url = "jdbc:mysql://127.0.0.1:3306/test";
private String username = "root";
private String password = "123456";
private String driver = "com.mysql.jdbc.Driver";
private Connection connection;
// 编译 sql 语句的
private Statement statement;
public JdbcMySql() {
try {
// 1. 构造函数最先执行,开始寻找 mysql 的驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public void open() {
// 2. 执行 open 函数,打开连接,连接 mysql 数据库
try {
connection = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// 3. 打开通道之后,我们就要执行数据库操作了
// crud
// 查询数据库 user 表
public List<User> query() {
List<User> list = new ArrayList<>();
try {
// 1. 获取编译 sql 的对象
statement = connection.createStatement();
String sql = "select * from t_users";
// 2. 执行 sql,得到结果
ResultSet resultSet = statement.executeQuery(sql);
// 3. 循环遍历结果
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String pwd = resultSet.getString("password");
// 4. 封装数据为对象
User user = new User();
user.setId(id);
user.setUsername(name);
user.setPassword(pwd);
// 5. 添加到集合中
list.add(user);
}
// 6. 得到的集合返回出去
return list;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
在写 query 函数时,我们并没有业务在里面,所谓业务就是一堆判断、不合法的获取等等。当涉及到了业务的时候,这个 query 函数代码就变得非常多,通常写一个业务会存在多个步骤,当所有步骤都写在一个函数里面,代码的阅读性就变得非常地低了。
所以,我们上面的代码可以拆分步骤,再将这些步骤组装到函数里面。
add:[private List<User> extractUserDataFromSet(ResultSet set) throws SQLException {
List<User> list = new ArrayList<>();
while (set.next()) {
int id = set.getInt("id");
String name = set.getString("username");
String pwd = set.getString("password");
User user = new User();
user.setId(id);
user.setUsername(name);
user.setPassword(pwd);
list.add(user);
}
return list;
}]:add
public List<User> query() {
try {
statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from t_users");
lit:[return extractUserDataFromSet(resultSet);]:lit
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
阅读函数名称 extractUserDataFromSet,就可以知道这个函数是专门做从结果集中抽取用户数据的。这样可以让我们一目了然,这个函数在做什么。当有其他处理步骤时,再写一个简明扼要的函数名称,去专门做这个事情。
tip:[start]
虽然这个函数抽离出来之后可能从始至终使用一次,但是可以提升我们代码的可阅读性和维护性。注意,这种函数的抽离并不会降低代码的耦合度。
tip:[end]
假如,有一个非常简单的业务处理:
public List<User> query() {
try {
statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from t_users");
List<User> list = extractUserDataFromSet(resultSet);
add:[if (list.isEmpty()) {
System.out.println("查询不到数据");
return null;
} else {
return list;
}]:add
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
如果这个业务非常复杂,代码也很多,不可能一直在这个类继续添加函数去处理,随着功能增加,我们的这个类就显得非常冗余、混乱。
所以,将代码分层书写就变得非常重要!