談論起「持久化」一詞,每天操做數據庫的你們確定不會陌生。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
就能囊括的?
業務是老大,若是真的碰到了高併發需求,那又是咱們進步的時候了。