一般项目建议必须使用orm框架,需要在意的只是框架选型的问题。
对于数据库的操作,我的学习流程是jdbc,spring-jdbc,mybatis。每种方式我都用过,用得最多的还是mybatis,而且我觉得用得最顺手。
现在想一想,mybatis,spring-jdbc其实都是对jdbc的一种封装。
既然可以使用以上两者,那么为什么要使用mybatis呢?
一、提出的一些问题
- mybatis到底节省了什么步骤?不是和jdbc一样要自己写sql语句吗?
- mybatis generator生成的sql语句,会不会不可靠?
- 使用mybatis可能会带来什么问题?
二、使用jdbc的一些体会
jdbc的简单示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
package com.xie.service.interfaces; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import com.xie.pojo.interfaces; import com.xie.service.dbConnection.dbConnection; public class interfacesServiceImpl implements interfacesService { private Connection connection = dbConnection.getConnection(); private Statement statement = null; private PreparedStatement preparedStatement = null; private ResultSet resultSet = null; @Override public interfaces selectByPath(String path) { // TODO Auto-generated method stub if (path == null || path == "") { return null; } String sql = "select * from interfaces where path = ?"; try { preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, path); resultSet = preparedStatement.executeQuery(); interfaces interfaces = new interfaces(); while (resultSet.next()) { interfaces.setId(resultSet.getInt("id")); interfaces.setPath(resultSet.getString("path")); interfaces.setDescription(resultSet.getString("description")); interfaces.setTime(resultSet.getDate("time")); return interfaces; } return null; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); return null; } finally { try { preparedStatement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } |
整个流程包括:
- 取得链接。
- 准备语句。
- 执行语句。
- 获得结果集。
- 自行包装结果。
- 关闭连接。
- 回收资源。
全部流程需要我们手动实现,每条不同的sql语句都需要不同的处理,缺少重用性,有很多代码冗余。
而且,一个项目往往有许多模块,一般是由很多人分开实现的,每个人编码方式可能不同,导致代码结构混乱。多人协作往往需要统一的编码规范,所有人照着这个规范来写,可以使代码结构一致,大大减少沟通成本。
所以,jdbc的代码还是需要稍微封装一下,每次要使用这个功能直接调用就好了。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
package test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Map; import com.xie.service.dbConnection.dbConnection; public class testJdbc { private Connection connection = dbConnection.getConnection(); private Statement statement = null; private PreparedStatement preparedStatement = null; private ResultSet resultSet = null; public ResultSet test(String sql, Map<Integer, String> params) { try { preparedStatement = connection.prepareStatement(sql); for (Integer key : params.keySet()) { preparedStatement.setString(key, params.get(key)); } resultSet = preparedStatement.executeQuery(); return resultSet; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); return null; } finally { try { resultSet.close(); preparedStatement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } |
这只是个例子,把查询部分独立提出来,获得结果集。
现在查询部分是封装好了,但是结果对象每次都还是需要我们自己封装呢。每次都要手写多麻烦,能不能把结果自动映射成结果对象(需要用到反射知识)?
- 自己创建要封装的结果对象(需要有set方法)。
- 传入到方法中。
- 方法中使用反射,获取对象,获取set方法,使用set方法。
- 得到映射出的对象。
- 返回映射出的对象。
封装是解决了,但是我觉得这里还是有一些问题。
sql语句总是写在java文件中。每个模块就对应着一部分代码。一旦功能多了起来,就会导致sql语句分散在各个java文件中,每次修改、优化、维护就一定要到源程序中更改sql语句,然后重新编译,才能实现功能。
所以,能不能通过配置文件之类的东西让我们可以统一管理,维护sql语句呢?能不能不要每次都修改源程序,每次都要重新编译,才能测试运行?
所以有如下解决方案:
- 最早的人使用properties配置文件来进行sql语句的记录。
- 有了xml后,xml文件成为了配置文件的标配。
- 配置文件根本不需要编译到程序中。
- 只要更改配置文件,就能影响运行中的程序更改程序的配置信息了。
之后又发现了一个问题…数据库链接只要被调用了就链接,用完了就释放,这样很浪费资源!能不能实现一个数据库连接池,每次需要链接就到里面取?用完了放回去保持状态而不释放?而且这个连接池的参数一定是可以根据业务需求而让开发人员自行配置的…
之后又发现了一个问题…光是sql语句难以完成较复杂的业务逻辑…我能不能在封装的时候在代码层自定义sql功能的拓展,比如判断,增加字段之类的?
又一个问题…每次写数据库功能都要从正向入手,从crud开始写起。能不能有这样的脚本,可以根据数据库的信息,自动生成crud的代码?
…
三、总之,随着需求的增多,对于数据库的操作有了如下要求
- 封装sql执行过程,结果可以自动映射出来。
- 需要多种自定义配置,用来拓展sql的逻辑不足。
- 需要代码的规范统一,减少重复代码的冗余。
- 需要独立的配置文件,方便维护管理。
- 甚至不需要自行编写sql和逻辑语句,可以由数据库脚本反向生成。
- 连接池等关于数据库链接的管理。
- …
四、为什么使用orm框架(使用mybatis举例)
回应上面的5个要求:
(1)mybatis存在mapper->dao->pojo的映射
结构清晰,可以通过映射文件和映射方法直接获取结果。
(2)存在逻辑语句
1 2 3 |
<if test="ip != null"> ip, </if> |
(3)统一的规范
一般的mvc框架,我习惯是分为6层。
- pojo实体类。
- mapper映射文件。
- dao被映射的方法。
- service中初步对方法做进一步的包装。
- 然后根据需求在service中写具体的业务实现。
- 在controller中写映射方法。
每种文件都有唯一的位置,所有人员开发的时候都遵循这个规范,可以使整个项目结构更加合理。
(4)mapper文件是独立的,方便对sql进行优化
sql文件独立配置。
(5)mybatis generator
常用的重复逻辑,使用自动生成。减少无谓的人工劳动。
(6)mybatis自带连接池,也可以整合别的数据源的连接池
…
五、一些问题
(1)myabtis generator默认select所有字段,增加了搜索的开销,不是最优解
如果只需要表中的某几个字段,那么自己手写sql就可以啦。
(顺带一提,似乎有这样一种说法:select * 比 select 所有字段 效率更低,我之前做了一次测试,感觉没有什么区别,select * 的查询速度甚至要更快。而且,似乎 select 部分字段 效率不一定比 select 所有字段 效率高,我也不是很清楚为什么)
(2)jdbc是原生方法,比起mybatis速度如何?
同情况下原生应该比封装要快。
有人测试过,认为:
1 |
JDBC > JDBC.Object > Spring.JDBC > MyBatis |
对于机器来说,速度肯定有快慢。对于人来说,都是在毫秒级,几乎没有区别。
牺牲部分性能,带来的是更多的好处。
(3)使用mybatis和使用原生jdbc,哪个成本更高?
从开发速度、维护难度、编码风格的角度来考虑,mybatis是更好的选择。
六、总结
假如某个java中间件性能要求特别高,使用jdbc也许更好。
假如有个复杂的系统,有非常多的sql需要维护,开发和维护成本很高,难以优化和拓展。这种情况下我认为应该使用mybatis。
总之,一般项目就应该使用成熟的orm解决方案。