谈论起“持久化”一词,每天操做数据库的你们确定不会陌生。php
持久化中,备受开发者瞩目的就是两大巨头:Hibernate
与MyBatis
,许多开发者也经常拿两者对比。去Google
上,都是相似二者互相对比的文章。java
Hibernate
与MyBatis
,就像java
和php
同样,双方似有打架之势。mysql
虽然我是使用Hibernate
的工程师,可是我并不否定MyBatis
,二者都优秀,但一切都是业务优先。程序员
学习了潘老师的《SpringBoot+Angular入门实例教程》后,也想到了最原始的JDBC
。spring
今天,咱们就好好地从古至今,聊聊持久化技术的演进与对比。sql
JDBC
:Java Database Connectivity
,简称JDBC
。是Java
语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC
是面向关系型数据库的。
每一位Java
程序员可能都经历过被JDBC
支配的恐惧。数据库
下面是我在Java
实验课上写过的JDBC
代码,实现的功能很简单,就是要将一个Student
对象保存到数据库中的student
表中。数组
/** * 持久化学生实体 */ private static void persistStudent(Student student) { try { Class.forName("com.mysql.jdbc.Driver"); // 数据库链接配置 String url = "jdbc:mysql://127.0.0.1:7777/java?characterEncoding=utf-8"; // 获取链接 Connection connection = DriverManager.getConnection(url, "root", "root"); // 获取语句 Statement statement = connection.createStatement(); // 生成SQL语句 String SQL = student.toSQLString(); // 执行语句 statement.executeUpdate(SQL); } catch (SQLException e) { System.out.println("ERROR: " + e.getMessage()); } catch (ClassNotFoundException e) { System.out.println("ERROR: " + e.getMessage()); } finally { statement.close(); connection.close(); } }
核心的功能其实就一行:statement.executeUpdate(SQL)
,我只想执行一条INSERT
语句保证数据的持久化。缓存
但是在JDBC
中却须要实现加载MySQL
驱动,获取Connection
,获取Statement
,才能执行SQL
,执行以后还须要手动释放资源,不可谓不麻烦。springboot
程序员最讨厌写重复的代码,就像我感受从华软平台移植重复代码到试题平台很枯燥同样,因此这些“模版式”的代码须要封装。
咱们都是平凡人,咱们能想到的,确定早就有人想到了。
Spring
封装了JDBC
,提供了JdbcTemplate
。
另外一个比较出名的是Apache
封装的DBUtils
。
使用了JdbcTemplate
后,咱们无需再编写模版式的Connection
、Statement
、try ... catch ... finally
等代码。教程中使用了JdbcTemplate
查询教师表,代码长这样:
@GetMapping public List<Teacher> getAll() { /* 初始化不固定大小的数组 */ List<Teacher> teachers = new ArrayList<>(); /* 定义实现了RowCallbackHandler接口的对象 */ RowCallbackHandler rowCallbackHandler = new RowCallbackHandler() { /** * 该方法用于执行jdbcTemplate.query后的回调,每行数据回调1次。好比Teacher表中有两行数据,则回调此方法两次。 * * @param resultSet 查询结果,每次一行 * @throws SQLException 查询出错时,将抛出此异常,暂时不处理。 */ @Override public void processRow(ResultSet resultSet) throws SQLException { Teacher teacher = new Teacher(); /* 获取字段id,并转换为Long类型返回 */ teacher.setId(resultSet.getLong("id")); /* 获取字段name,并转换为String类型返回 */ teacher.setName(resultSet.getString("name")); /* 获取字段sex,并转换为布尔类型返回 */ teacher.setSex(resultSet.getBoolean("sex")); teacher.setUsername(resultSet.getString("username")); teacher.setEmail(resultSet.getString("email")); teacher.setCreateTime(resultSet.getLong("create_time")); teacher.setUpdateTime(resultSet.getLong("update_time")); /* 将获得的teacher添加到要返回的数组中 */ teachers.add(teacher); } }; /* 定义查询字符串 */ String query = "select id, name, sex, username, email, create_time, update_time from teacher"; /* 使用query进行查询,并把查询的结果经过调用rowCallbackHandler.processRow()方法传递给rowCallbackHandler对象 */ jdbcTemplate.query(query, rowCallbackHandler); return teachers; }
思考问题:既然能够经过ResultSet
获取查询结果,这里为何Spring
封装的query
方法不直接返回ResultSet
对象,而要设计这样一个回调的对象呢?
提示,JdbcTemplate
查询的核心源码以下:
@Override @Nullable public T doInStatement(Statement stmt) throws SQLException { ResultSet rs = null; try { rs = stmt.executeQuery(sql); return rse.extractData(rs); } finally { JdbcUtils.closeResultSet(rs); } }
ResultSet
用起来很讨厌是否是?须要手动地去获取字段并设置到对象中,JdbcTemplate
提升了开发效率,可是提升地不明显,能不能再简单一点呢?
为了不编写大量这样的与业务无关的代码,设计了ORM
思想。
teacher.setName(resultSet.getString("name")); teacher.setSex(resultSet.getBoolean("sex")); teacher.setUsername(resultSet.getString("username")); teacher.setEmail(resultSet.getString("email"));
ORM
:即对象关系映射。将对象与数据表进行关联,咱们无需关注这类不关联业务的冗余代码,操做对象,即操做数据表。
半自动化的ORM
框架,就是煊赫一时的MyBatis
。
我简单地去学习了如下MyBatis
,毕竟这么多公司使用,确定有他们的道理,若是足够优秀,也能够考虑使用。但是结果却有些使人失望。
打开官网学习,这应该算是我见过的最寒酸的著名开源框架的官网了,里面的内容也不详细。
官网的例子不够详细,我又参阅了许多MyBatis
的博文进行学习。
开启数据库字段下划线到对象命名驼峰的配置。
mybatis.configuration.map-underscore-to-camel-case=true
仍是经典的教师、班级、学生的关系:
public class Teacher { private Long id; private String name; private Boolean sex; private String username; private String email; private Long createTime; private Long updateTime; } public class Klass { private Long id; private String name; private Teacher teacher; private List<Student> students; } public class Student { private Long id; private String name; }
教师的CRUD
单表查询:
@Mapper public interface TeacherMapper { @Select("SELECT * FROM teacher") List<Teacher> getAll(); @Select("SELECT * FROM teacher WHERE id = #{id}") Teacher get(Long id); @Select("SELECT * FROM teacher WHERE username = #{username}") Teacher findByUsername(String username); @Insert("INSERT INTO teacher(name, sex, username, email, create_time, update_time) VALUES(#{name}, #{sex}, #{username}, #{email}, #{createTime}, #{updateTime})") @Options(useGeneratedKeys = true, keyProperty = "id") void insert(Teacher teacher); @Update("UPDATE teacher SET name=#{name}, sex=#{sex}, email=#{email}, update_time=#{updateTime} WHERE id=#{id}") void update(Teacher teacher); @Delete("DELETE FROM teacher WHERE id=#{id}") void delete(Long id); }
关联查询:
@Mapper public interface KlassMapper { @Select("SELECT * FROM klass") @Results({ @Result(column = "teacher_id", property = "teacher", one = @One(select = "club.yunzhi.mybatis.mapper.TeacherMapper.get")), @Result(column = "id", property = "students", many = @Many(select = "club.yunzhi.mybatis.mapper.StudentMapper.getAllByKlassId")) }) List<Klass> getAll(); }
关联中用到的StudentMapper
子查询:
@Mapper public interface StudentMapper { @Select("SELECT * FROM student WHERE klass_id=#{klassId}") List<Student> getAllByKlassId(Long klassId); }
也学了一个晚上了,虽然二级缓存之类的高级特性还没学习呢,可是对MyBatis
也算是又一个大致的了解了。
业务是老大,全部技术都是服务于业务的。程序员应该关注业务与实际问题,我是不太喜欢去写SQL
的,SQL
应该是DBA
去专业学习并优化的,MyBatis
看起来更像是给DBA
用的框架同样。
当一个系统设计完成以后,其余的工做就是搬砖。
搬砖固然是越简单越好,不编写SQL
,Hibernate
走起。
public interface TeacherRepository extends CrudRepository<Teacher, Long> { }
彻底基于对象,更加注重业务。
好多人都是“你看支付宝用MyBatis
,那我也用MyBatis
”。
这是StackOverflow
上一个十年前的关于二者对比讨论的话题:https://stackoverflow.com/questions/1984548/hibernate-vs-ibatis
最终的讨论结果以下:
iBatis
和Hibernate
是彻底不一样的事物(iBatis
是MyBatis
的前身)。
若是以对象为中心,那Hibernate
更好;若是以数据库为中心,那iBatis
更好。
若是你设计系统架构,没有高并发的需求,Hibernate
最合适,对象模型会使得代码很是简洁,但代价巨大。
若是你接手了一个遗留下来的数据库,而且须要编写复杂的SQL
查询,那iBatis
更合适。
总结一句话就是性能问题:Hibernate
方便,可是会有性能损耗;MyBatis
有性能优点。
都说MyBatis
适合高并发,但高并发又岂是一个MyBatis
就能囊括的?
业务是老大,若是真的碰到了高并发需求,那又是咱们进步的时候了。