1. 程式人生 > >MyBatis+Hibernate+JDBC對比分析

MyBatis+Hibernate+JDBC對比分析

MyBatis目前作為持久層,用的最多,因為它符合網際網路開發的變動性,實際開發中需求總會有這樣的,那樣的變動,MyBatis雖然沒有Hibernate那麼全自動化,而且對於開發人員的sql能力要求比較高,但是正是因為這樣sql可以優化,業務也更容易擴充套件。

hibernate對於sql能力不是特別強的開發人員,是一個很好的利器,而且hibernate更面向物件,hibernate不適合開發高併發高訪問量的應用,很簡單不能sql優化,每次查詢基本都要全表掃描,即便是有hql,但是過多的使用hql,反而破壞Hibernate的封裝性,實際開發多聯表查多個欄位資訊,有的時候一張表十幾個欄位,資訊,另外一個表十幾個,在另外一個表又十幾個,對於Hibernate而言是個不小的挑戰,對於效能要求不高,響應速度較慢的應用,同一時間段訪問人不多的應用,還是很不錯的,可以大幅度提高開發效率

OA辦公,ERP等應用比較適合用Hibernate開發

像入口網站,部落格這樣的,訪問量比較大,不只是前端優化,後臺也要優化,所謂的後臺主要是指sql優化,當然還有Java程式碼方面

MyBatis相對於JDBC而言,要好多了,至少它實現瞭解耦,sql語句和Java程式碼分離。

對於目前我個人部落格專案,它的逆向工程讓我不再重複單表的增刪改查,節省比較多的時間,當然mybatis,雖然對於需求變動大的專案而言是比較符合實際的需要的,但是它的不好之處也顯現出來了,就是對於不同的資料庫需要書寫不同的sql語句,可移植性差,而且它的外掛用不好的話,導致效能也會下降

閒話不多說,貼一波個人編寫的程式碼,溫故而知新吧

我這邊演示是JDK8+MAVEN工程

pom檔案內容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>cn.example.test</
groupId> <artifactId>mybatis</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <!-- hibernate核心包 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.12.Final</version> </dependency> <!--mybatis核心包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.1.1</version> </dependency> <!-- 連線Mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.21</version> </dependency> <!--列印日誌 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> </project>

一、JDBC示例

package cn.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JdbcExample {
    
    private static Connection getConnection() throws Exception {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            
            String url = "jdbc:mysql://localhost:3306/ssm";
            String username = "root";
            String password = "1234";
            conn = DriverManager.getConnection(url, username, password);
        
            
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        
            return null;
        }
        return conn;
            
        
    }
    
    public User getUserById(Integer Id) throws Exception {
        /**
         * 頻繁的建立連線
         */
        Connection connection = getConnection();
        
        PreparedStatement ps = null;
        
        ResultSet rs = null;
        
        try {
            /**
             * Java程式碼和sql混合,不解耦,隨著專案擴大,工作量會成倍增加
             */
            ps = connection.prepareStatement("select * from `user` where id=?");
            ps.setInt(1,Id);
            rs = ps.executeQuery();
        
            
            while(rs.next()) {
                /**
                 * 指定獲取的屬性 
                 * 實際開發中資料表的欄位多,想要獲取十幾二十幾個欄位資訊,如下rs.getInt("欄位名")等等一系列不知要寫多少個,隨著程式碼量的增大,更不利於排錯
                 */
                Integer id = rs.getInt("id");
                String userName  = rs.getString("user_name");
                User user = new User();
                user.setId(id);
                user.setUserName(userName);
                
                return user;
                
            }
                    
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            /**
             * 頻繁的關閉連線
             */
            this.closeAll(rs, ps, connection);
        }
    
        return null;
        
    }
    
    private void closeAll(ResultSet rs,PreparedStatement ps,Connection conn) {
        
        try {
            
            if(rs!=null && rs.isClosed()) {
                rs.close();
            }
            
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        
        try {
            
            if(ps!=null && ps.isClosed()) {
                ps.close();
            }
            
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        
        try {
            
            if(conn!=null && conn.isClosed()) {
                conn.close();
            }
            
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        
            
    }
    
    public static void main(String[] args) throws Exception {
        JdbcExample jdbc = new JdbcExample();
        
        User user = jdbc.getUserById(1);
    
        System.out.println(user.getUserName());
    }
    
    /**
     * 從上述程式碼我們可以總結出Java連線JDBC的步驟
     * 1.載入驅動
     * 2.建立連線
     * 3.通過PreparedStatement執行sql
     * 4.由ResultSet返回結果集
     * 5.關閉連線
     * 
     * 使用傳統的JDBC,存在如下問題:
     * 1.不解耦,JDBC和sql混合一起,當需求變動,既要改Java程式碼,又要修改sql語句,工作量十分大,而在實際專案中,需求是不斷變化的
     * 2.頻繁的建立連線,關閉連線,雖然後面有連線池,或者是將其封裝成一個Utils,不過程式碼量依然會很大
     * 
     * 說明:實際開發中並不用它,但是作為開發者必須要知道技術的應用場景和利弊,這樣才能在開發中遊刃有餘
     */
    

}

JavaBean

說明:JavaBean在該例子中是適用所有的

資料表字段也就兩個屬性,大家自己建即可,這裡就不貼了

package cn.jdbc;

public class User {
    private Integer Id;
    private String userName;
    public Integer getId() {
        return Id;
    }
    public void setId(Integer id) {
        Id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    
}

二、Hibernate示例

1.hibernate.cfg.xml主配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- property 元素用於配置Hibernate中的屬性
            鍵:值 
          -->
          <!-- hibernate.connection.driver_class : 連線資料庫的驅動  -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>


          <!-- hibernate.connection.username : 連線資料庫的使用者名稱 -->
        <property name="hibernate.connection.username">root</property>


          <!-- hibernate.connection.password : 連線資料庫的密碼 -->
        <property name="hibernate.connection.password">1234</property>


          <!-- hibernate.connection.url : 連線資料庫的地址,路徑 -->
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/ssm</property>



        <!-- show_sql: 操作資料庫時,會 向控制檯列印sql語句 -->
        <property name="show_sql">true</property>

    

        <!-- format_sql: 列印sql語句前,會將sql語句先格式化  -->
        <property name="format_sql">true</property>


      

        <!-- hbm2ddl.auto: 生成表結構的策略配置
             update(最常用的取值): 如果當前資料庫中不存在表結構,那麼自動建立表結構. 
                     如果存在表結構,並且表結構與實體一致,那麼不做修改
                     如果存在表結構,並且表結構與實體不一致,那麼會修改表結構.會保留原有列.
             create(很少):無論是否存在表結構.每次啟動Hibernate都會重新建立表結構.(資料會丟失)
             create-drop(極少): 無論是否存在表結構.每次啟動Hibernate都會重新建立表結構.每次Hibernate執行結束時,刪除表結構.
             validate(很少):不會自動建立表結構.也不會自動維護表結構.Hibernate只校驗表結構. 如果表結構不一致將會丟擲異常.
          -->
        <property name="hbm2ddl.auto">update</property>


        <!-- 資料庫方言配置 
         org.hibernate.dialect.MySQLDialect (選擇最短的)
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>


       

        <!-- hibernate.connection.autocommit: 事務自動提交  -->
        <property name="hibernate.connection.autocommit">true</property>



        <!-- 將Session與執行緒繫結=> 只有配置了該配置,才能使用getCurrentSession -->
        <property name="hibernate.current_session_context_class">thread</property>




        <!-- 引入ORM 對映檔案 
            填寫src之後的路徑
         -->
        <mapping resource="cn/hibernate/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

2.User.hbm.xml檔案

<hibernate-mapping>    
    
    <!--     
        <class>:使用class元素定義一個持久化類。    
        name="cn.javass.user.vo.UserModel":持久化類的java全限定名;    
        table="tbl_user":對應資料庫表名;    
        mutable="true":預設為true,設定為false時則不可以被應用程式更新或刪除;    
        dynamic-insert="false":預設為false,動態修改那些有改變過的欄位,而不用修改所有欄位;    
        dynamic-update="false":預設為false,動態插入非空值欄位;    
        select-before-update="false":預設為false,在修改之前先做一次查詢,與使用者的值進行對比,有變化都會真正更新;    
       
     -->    
    <class name="cn.hibernate.User" table="user" dynamic-insert="true" dynamic-update="true">    
            
        <!--     
            <id>:定義了該屬性到資料庫表主鍵欄位的對映。    
            name="userId":標識屬性的名字;    
            column="userId":表主鍵欄位的名字,如果不填寫與name一樣;    
         -->    
        <id name="Id">    
            <!-- <generator>:指定主鍵由什麼生成,推薦使用uuid(隨機生成唯一通用的表示符,實體類的ID必須是String),  
                               native(讓資料庫自動選擇用什麼生成(根據底層資料庫的能力選擇identity,sequence或hilo中的一種)),  
                               assigned(指使用者手工填入,預設)。 -->    
            <generator class="native"/>    
        </id>    

        <!--     
            <property>:為類定義一個持久化的javaBean風格的屬性。    
            name="name":標識屬性的名字,以小寫字母開頭;    
            column="name":表主鍵欄位的名字,如果不填寫與name一樣;    
            update="true"/insert="true":預設為true,表示可以被更新或插入;    
         -->    
        <property name="userName" column="user_name" />    
             
    </class> 
</hibernate-mapping>

3.HibernateExample.java

package cn.hibernate;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateExample {

    private static SessionFactory sessionFactory;
    
    static {
        
    
        try {
            Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
            
            sessionFactory = cfg.buildSessionFactory();
            
        } catch (Exception e) {
            // TODO: handle exception
        
            e.printStackTrace();
        }
    }
    
    public static SessionFactory getSessionFactory() {
        
        return sessionFactory;            
    }
    
    
    
    
    
    
}

4.TestHibernate.java

package cn.hibernate;

import org.hibernate.Session;

public class TestHibertnate {
    
    /**
     * hibernate流程
     *1.通過Configuration建立SessionFactory
     *2.通過SessionFactory建立Session
     *3.通過Session開啟openSession
     *4.通過session呼叫方法
     *
     *
     *openSession和getCurrentSession的區別
     *openSession建立session時autoCloseSessionEnabled引數為false,即在事物結束後不會自動關閉session,需要手動關閉,如果不關閉將導致session關聯的資料庫連線無法釋放,最後資源耗盡而使程式掛掉。              
     *getCurrentSession建立session時autoCloseSessionEnabled,flushBeforeCompletionEnabled都為true,並且session會同sessionFactory組成一個map以sessionFactory為主鍵繫結到當前執行緒。
     * @param args
     */
    public static void main(String[] args) {
        
        Session session = null;
        
        try {
            session = HibernateExample.getSessionFactory().openSession();
            
            User user = session.get(User.class, 1);
            System.out.println(user.getUserName());
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }finally {
            if(session!=null) {
                session.close();
            }
        }
        
        
    }
}

三、MyBatis示例

1.MyBatisExample示例

package cn.mybatis;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisExample {

    private static SqlSessionFactory sqlSessionFactory = null;
    
    public static SqlSessionFactory getSqlSessionFactory() {
        InputStream is  = null;
        if(sqlSessionFactory==null) {
            
            String resource = "mybatis-config.xml";
            try {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource));
                return sqlSessionFactory;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }            
    
        }
    
        return sqlSessionFactory;
    }
        
    
    

}

2.TestMyBatis.java

package cn.mybatis;

import org.apache.ibatis.session.SqlSession;

public class TestMyBatis {

    public static void main(String[] args) {
        SqlSession sqlSession = null;
        sqlSession = MyBatisExample.getSqlSessionFactory().openSession();
        
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.getUserById(1);
        /**
         * 通過Main方法執行只能顯示ID資訊,不能顯示userName的資訊
         * 這是因為resultType針對的欄位和屬性一致的情況,當資料表字段與JavaBean屬性不一致時,要得到資料,需要使用resultMap
         */
        System.out.println(user.getId()+"||"+user.getUserName());

    }
}

3.UserMapper.java

package cn.mybatis;

public interface UserMapper {

    
    User getUserById(Integer Id);
}

4.UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.UserMapper">

    <!-- 
    id 必須與對應介面方法一致
    parameterType 引數型別可以是Map,String,Integer等等一大堆,但是必須要指定
    resultType 適用場景:資料表字段和JavaBean實體屬性一致
    resultMap 適用場景:資料表字段和JavaBean實體屬性不一致
    
     -->
    
    <!--     <select id="getUserById" parameterType="Integer" resultType="cn.mybatis.User">
        select id,user_name from `user` where id=#{Id}
    </select> -->
    
    <select id="getUserById" parameterType="Integer" resultMap="users">
        select id,user_name from `user` where id=#{Id}
    </select>    
    
    <!-- 
    column對應資料表字段
    property對應JavaBean屬性
    type 通常指返回型別 一般要寫全限定名 不過只要在mybatis-config.xml配置別名掃描就不要寫全名了
     -->
     
    <resultMap type="User" id="users">
    <id column="id" property="Id"/>
    <result column="user_name" property="userName"/>
    </resultMap>

</mapper>

5.mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    
    <!-- 給cn.mybatis包下起別名 -->
    <typeAliases>
    <package name="cn.mybatis" />
    </typeAliases>

     <!-- 對事務的管理和連線池的配置 -->  
    <environments default="development">  
        <environment id="development">  
            <transactionManager type="JDBC" />  
            <dataSource type="POOLED">  
                <property name="driver" value="com.mysql.jdbc.Driver" />  
                <property name="url" value="jdbc:mysql://localhost:3306/ssm" />  
                <property name="username" value="root" />  
                <property name="password" value="1234" />  
            </dataSource>  
        </environment>  
    </environments>  
      
    <!-- mapping 檔案路徑配置 -->  
    <mappers>  
        <mapper resource="cn/mybatis/UserMapper.xml" />  
    </mappers>  

</configuration>  

MyBatis還有很多有待研究的,說到這,不得不提一個增強版本的MyBatis 稱之為MyBatis Plus 可無縫結合Spring+SpringMVC,而且增刪改查也不需要寫,只需繼承一個類即可

參考官網:http://mp.baomidou.com/

關於mybatis的逆向工程和動態代理,可以參考我的部落格Java框架和MyBatis