1. 程式人生 > 其它 >Hibernate 與 Mybatis 入門使用

Hibernate 與 Mybatis 入門使用

技術標籤:Javahibernatemybatis

Title: Hibernate 與 Mybatis 入門使用
Date: 2018-7-12 16:48
Category: 技術部落格
Modified: 2018-7-12 16:48
Tags: Java-Web
Slug: Hibernate-mybatis-use
Authors: Victor Lv
Summary: Hibernate 與 Mybatis 入門使用

1. Hibernate – 基於 Hibernate 5.3.1 final

Hibernate 核心配置檔案 hibernate.cfg.xml

hibernate.cfg.xml 是必備的配置檔案,用於配置資料庫連線資訊(包括我們在 JDBC 中用到的 driver、url、username、password 以及其他的連線配置),以及指定 ORM 配置檔案路徑。示例如下:

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
name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/lvlang?" +"autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=UTF-8</property> <property
name="hibernate.connection.username">
root</property> <property name="hibernate.connection.password">123456</property> <property name="hibernate.show_sql">true</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.use_sql_comments">true</property> <!-- 指定 ORM 配置檔案 --> <mapping resource="config/User.hbm.xml" /> <mapping resource="config/Student.hbm.xml" /> </session-factory> </hibernate-configuration>

另外,hibernate.cfg.xml 須置於 工程或 classpath 根目錄才能讓程式找到它。本文測試工程結構如下(使用IntelliJ IDEA):
Hibernate 測試工程結構

ORM 配置檔案 ClassName.hbm.xml :

Hibernate 作為一個 ORM (Object Relational Mapping – 物件–關係型資料庫 對映),它使用 ClassName.hbm.xml 配置檔案來作為 資料庫表 和 Java類的對映配置,本文以兩個簡單的資料表 (User 和 Student 表)為例,表結構如下:
User 和 Student 表

其中 User 表對應的對映檔案 User.hbm.xml 如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="hibernate">
	<!-- Table: User -->
    <class name="User" table="User" catalog="lvlang">
		<id name="id" column="id" type="int" >
            <!-- Declaim that the primary key is auto-generated -->
			<generator class="native" />
		</id>
		<property name="name" column="name" length="20" />
		<property name="address" column="address" length="50" />
	</class>
</hibernate-mapping>

表示該主鍵元素值由資料庫自動生成。

Student 表對應的對映檔案 Student.hbm.xml 如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="hibernate">
    <!-- Table: Student -->
    <class name="Student"  table="Student" catalog="lvlang">
        <composite-id name="StudentID" class="StudentID">
            <key-property name="id" type="int">
                <column name="id" />
                <!-- Declaim that the primary key is auto-generated -->
            </key-property>
            <key-property name="name" type="java.lang.String" length="20">
                <column name="name" />
            </key-property>
        </composite-id>
        <property name="address" column="address" type="java.lang.String" length="100" />
        <property name="score" column="score" type="int" />
    </class>
</hibernate-mapping>

Hibernate 的 ORM 配置檔案一目瞭然,這是它的一個優勢。

對映類 ClassName.java

ORM 中的 資料表有了,對映關係也配置好了,就剩實現對應的 Java 類了:
User.java

/**
 * @ClassName: User.java
 * @Description:
 * @Author: Victor Lv (http://langlv.me)
 * @Email: [email protected]
 * @Date: Jul 2, 2018
 * @Version: V1.0
 */
package hibernate;

/**
 * @ClassName: User.java
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/10 15:44
 * @Version: 1.0
 */

public class User {
    private int id;
    private String name;
    private String address;

    public User() {
    }

    public User(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the address
     */
    public String getAddress() {
        return address;
    }

    /**
     * @param address the address to set
     */
    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User {id=" + id + ", name=" + name + "}";
    }
}

Student.java,Student 表使用了複合主鍵,所以它的 primary key 在這裡用專用的類封裝了:

package hibernate;

/**
 * @ClassName: Student
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/11 11:24
 * @Version: 1.0
 */

public class Student {

    private StudentID studentID;
    private String address;
    private int score;

    public Student() {
    }

    public Student(int id, String name, String address, int score) {
        StudentID ID = new StudentID(id, name);
        this.studentID = ID;
        this.address = address;
        this.score = score;
    }

    public Student(StudentID ID, String address, int score) {
        this.studentID = ID;
        this.address = address;
        this.score = score;
    }

    public StudentID getStudentID() {
        return studentID;
    }

    public void setStudentID(StudentID studentID) {
        this.studentID = studentID;
    }

    public String getAddress() {
        return address;
    }

    public int getScore() {
        return score;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{id = " + getStudentID().getId() + " name = " + getStudentID().getName()
                + " address = " + getAddress() + " score = " + getScore() +" }";
    }
}

對應的 StudentID.java

package hibernate;

import java.io.Serializable;

/**
 * @ClassName: StudentID
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/11 11:16
 * @Version: 1.0
 */

public class StudentID implements Serializable {

    private int id;
    private String name;

    public StudentID(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public StudentID() {
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }
}

SessionFactory——Session 生產工廠

一個 session 就是客戶端與伺服器的一次會話,SessionFactory 的存在使得 session 的建立變得輕便,好比一壺配好了茶葉的茶,需要時從壺裡倒一杯出來即可,請君品鑑,而不是來了一位新客人就得重頭開始泡一壺茶。

Hibernate 的SessionFactory 和 Session 管理非常簡便,下述 MySessionFactory.java 封裝 SessionFactory 的建立和 Session 的獲取功能,SessionFactory 僅需一個,所以定義成 static 屬性,在類載入時即建立。

MySessionFactory.java:

package hibernate;

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

/**
 * @ClassName: MySessionFactory.java
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/10 15:44
 * @Version: 1.0
 */

public class MySessionFactory {

    private static SessionFactory sessionFactory;

    static {
        //1. find and import the hibernate.cfg.xml configuration
        Configuration config = new Configuration().configure();

        //2. build session factory
        sessionFactory = config.buildSessionFactory();
    }

    public static Session getSession() {
        return sessionFactory.openSession();
    }

}

簡單使用–HibernateTest

茶已泡好了一壺,萬事胥備,就等客人了。session 的管理尚有 beginTransaction (開啟茶壺蓋)、 commit (倒茶入杯) 、 close session(合上茶壺蓋) 這一系列固定流程,那再僱傭一個服務員來做這一系列的事吧,就叫它 Mysession.java :

MySession.java:

package hibernate;

import org.hibernate.Session;

import java.io.Serializable;

/**
 * @ClassName: MySession
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/10 17:23
 * @Version: 1.0
 */

public class MySession {

    /* 加雙重校驗鎖的懶漢單例模式:
    (參考:https://blog.csdn.net/goodlixueyong/article/details/51935526)

    1、volatile保證了uniqueInstance的修改對各個執行緒的可見性。

    2、這是個static方法synchronized(this)肯定是不行的,因為沒有this(this針對的是例項化物件)。
        再說synchronized(uniqueInstance),synchronized是針對物件而言的,物件都是堆裡的物件,
        但是初始化狀態下uniqueInstance是null,只是棧裡的一個標識,在堆裡沒有。
        synchronized(null)會報空指標異常。
     */

    private static volatile MySession instance = null;

    public static MySession getInstance() {
        if (null == instance) {
            synchronized (MySession.class) { //synchronized 保證了一次只有一個執行緒能進入下面的程式碼塊

                /* 雙重校驗鎖:因為上頭的 if (null == instance) 無執行緒鎖,
                    假設執行緒 A 和 B 同時越過了上頭的校驗,來到了 synchronized 程式碼塊,
                    執行緒 A 先執行了程式碼塊並 new 了一個 instance ,
                    然後執行緒 B 進來了,如果此處不加雙重校驗,那麼執行緒 B 會認為 (null == instance) 仍然是成立的,
                    所以執行緒 B 也 new 了一個 instance。
                 */
                if (null == instance) {
                    return new MySession();
                }
            }
        }
        return instance;
    }

    private Object execute(Object object, String command, Serializable id) {
        //3. open (create) a session (DB connection)
        Session session = MySessionFactory.getSession();

        //4. begin transaction
        session.beginTransaction();

        //5. execute the task
        Object result = new Object();
        if ("add".equals(command))
            session.save(object);
        else if ("update".equals(command))
            session.update(object);
        else if ("delete".equals(command)) {
            session.delete(object);
        } else if ("load".equals(command))
            result = session.load(object.getClass(), id);
        else if ("get".equals(command))
            result = session.get(object.getClass(), id);

        //6. commit the transaction
        session.getTransaction().commit();

        //7. close session
        session.close();

        //8. close session factory
//        sessionFactory.close();

        return result;
    }

    public void add(Object object) {
        execute(object, "add", null);
    }

    public void update(Object object) {
        execute(object, "update", null);
    }

    public void delete(Object object) {
        execute(object, "delete", null);
    }

    public Object load(Object object, Serializable id) {
        return execute(object, "load", id);
    }

    public Object get(Object object, Serializable id) {
        return execute(object, "get", id);
    }

}

下面就是客人來喝茶了,寫了幾個簡單的 Hibernate 使用樣例:

HibernateTest.java:

    package hibernate;

    import org.hibernate.Session;

    import java.io.Serializable;

    /**
     * @ClassName: HibernateTest.java
     * @Description: TODO
     * @Author: http://langlv.me
     * @Date: 2018/7/10 15:44
     * @Version: 1.0
     */

    public class HibernateTest {

        public static void main(String[] args) {
            userTest();
            studentTest();
        }

        public static void userTest() {
            //----- 1. User table ------
            // add one data
            User userAdd = new User(0, "Victor", "Guangdong");
            MySession.getInstance().add(userAdd);

            // query data
            User myUser1 = (User) MySession.getInstance().get(new User(), 1);
            System.out.println(myUser1.toString());

            // update data
            User userUpdate = new User(1, "Bill Gates", "Beijing");
            MySession.getInstance().update(userUpdate);

            // query data
            User myUser2 = (User) MySession.getInstance().get(new User(), 1);
            System.out.println(myUser2.toString());

            // delete one data
            User userDelete = new User(3, null, null);
            MySession.getInstance().delete(userDelete);
        }

        public static void studentTest() {
            //----- 2. Student table ------
            // add data
            Student studentAdd1 = new Student(1, "Bill Gates", "New-York", 100);
            Student studentAdd2 = new Student(2, "Tony", "Beijing", 99);
            Student studentAdd3 = new Student(3, "Victor", "Shanghai", 98);
            MySession.getInstance().add(studentAdd1);
            MySession.getInstance().add(studentAdd2);
            MySession.getInstance().add(studentAdd3);

            //query data
            StudentID ID1 = new StudentID(1, "Bill Gates");
            Student myStudent1 = (Student) MySession.getInstance().get(new Student(), ID1);
            System.out.println((null == myStudent1) ? "Empty" : myStudent1.toString());

            //update data
            Student studentUpdate = new Student(1, "Bill Gates", "Miami", 95);
            MySession.getInstance().update(studentUpdate);

            //query data
            StudentID ID2 = new StudentID(1, "Bill Gates");
            Student myStudent2 = (Student) MySession.getInstance().get(new Student(), ID2);
            System.out.println((null == myStudent2) ? "Empty" : myStudent2.toString());

            //delete data
            Student studentDelete = new Student(2, "Tony", null, 0);
            MySession.getInstance().delete(studentDelete);

            //query data
            StudentID ID3 = new StudentID(2, "Tony");
            Student myStudent3 = (Student) MySession.getInstance().get(new Student(), ID3);
            System.out.println((null == myStudent3) ? "Empty" : myStudent3.toString());
        }

    }

對於入門級別的簡單使用,Hibernate 的整個流程下來一氣呵成,幹練明瞭,一條 SQL 語句也不用寫,只是在使用 Session 的諸如 get、load、save、update、delete 等各種方法時有點小糾結。

2. Mybatis —— 基於 Mybatis-3.4.6

MyBatis 核心配置檔案

與 Hibernate 類似,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>

    <!-- 引入外部配置檔案 -->
    <properties resource="DBConfig.properties"></properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${JDBC.driverClass}"/>
                <property name="url" value="${JDBC.url}"/>
                <property name="username" value="${JDBC.userName}"/>
                <property name="password" value="${JDBC.userPassword}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

這裡把資料庫的 driverClass、url、username 等資訊配置成從外部配置檔案 DBConfig.properties 讀取:

DBConfig.properties:

JDBC.driverClass=com.mysql.jdbc.Driver
JDBC.url=jdbc:mysql://localhost:3306/lvlang?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=UTF-8
JDBC.userName=root
JDBC.userPassword=123456

本文的測試工程結構如下(使用 Maven 工程形式):

Mybatis 測試工程結構

ORM 配置檔案—— xxxMapper.xml

上述 config.xml 配置指定了一個對映關係配置檔案 UserMapper.xml ,配置了 User 表(表結構和上述的例子相同)的SQL操作對映關係:

<?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="UserMapper">

    <select id="selectUser" parameterType="int" resultType="mybatis.User">
        SELECT * FROM User WHERE id = #{id}
    </select>

    <select id="selectUserName" parameterType="int" resultType="mybatis.User">
        SELECT name FROM User WHERE id = #{id}
    </select>

    <insert id="insertUser" parameterType="mybatis.User" useGeneratedKeys="true" keyProperty="id">
        insert into user (name, address)
                VALUES (#{name}, #{address})
    </insert>
    
    <delete id="deleteUser" parameterType="int" >
        DELETE FROM user WHERE id = #{id}
    </delete>

    <update id="updateUser" parameterType="mybatis.User">
        UPDATE user set
            name = #{name},
            address = #{address}
        where id = #{id}
    </update>
</mapper>

與 Hibernate 的對映關係配置檔案不一樣,Mybatis 的這個配置檔案主要配置的是 SQL 語句的 key-value 選項,也就是通過唯一的 key - String,能拉出來對應的 SQL 語句出來執行,同時在該配置檔案中指定 SQL 傳入引數資訊(比如引數型別)以及 resultMap 對應的 POJO 對映。
Hibernate 提供了 SQL 語句層的封裝,可以將資料庫操作自動生成 SQL 語句(也提供 HQL 的方式編寫自定義 SQL),對於簡單的資料庫操作非常方便;而 Mybatis 則交由程式設計師自行編寫 SQL 語句,靈活性更大。兩個框架的這個特性的差異,也引發了網路上就這兩個框架的開發效率、效能、可拓展性等展開過熱議。

對映類 – User.java

User.java:


/**
 * @ClassName: User.java
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/10 15:44
 * @Version: 1.0
 */

package mybatis;

public class User {
    private int id;
    private String name;
    private String address;

    public User() {
    }

    public User(int id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the address
     */
    public String getAddress() {
        return address;
    }

    /**
     * @param address the address to set
     */
    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name +", address= " + address + " ]";
    }
}

SessionFactory

與 Hibernate 自動讀取根目錄的 hibernate.cfg.xml 配置來產生 SessionFactory 不同,Mybatis 引入核心配置的方式更多樣,比如下述樣例,讀取指定的配置檔案成 InputStream,再用這個 InputStream構造出一個 SessionFactory:

SessionFactory.java:

package mybatis;

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

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

/**
 * @ClassName: SessionFactory.java
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/10 15:44
 * @Version: 1.0
 */

public class SessionFactory {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        String resource = "config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }

}

Mybatis 簡單使用 – MybatisTest

茶已泡好,請君入席:


package mybatis;

import org.apache.ibatis.session.SqlSession;
import java.io.IOException;

/**
 * @ClassName: MybatisTest.java
 * @Description: TODO
 * @Author: http://langlv.me
 * @Date: 2018/7/10 15:44
 * @Version: 1.0
 */

public class MybatisTest {

    public static void main(String arg[]) throws IOException {
        User myUser1 = selectUserName(7);
        System.out.println(myUser1.toString());

        System.out.println("Insert user: " + insertUser("langlv", "China"));

        System.out.println("Update user: " + updateUser(7, "Bill Gates", "Beijing"));
        User myUser2 = selectUser(7);
        System.out.println(myUser2.toString());

        System.out.println("Delete User: " + deleteUser(12));

    }

    public static User selectUserName(int id) {
        SqlSession session = SessionFactory.getSession();

        System.out.println("Selecting user...");
        try {
            User user = (User) session.selectOne((String) "UserMapper.selectUserName", id);
            if (null != user) {
//                System.out.println(user.toString());
                return user;
            } else {
//                System.out.println("Not found!");
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            session.rollback();
            return null;
        } finally {
            session.close();
        }
    }

    public static User selectUser(int id) {
        SqlSession session = SessionFactory.getSession();

        System.out.println("Selecting user...");
        try {
            User user = (User) session.selectOne((String) "UserMapper.selectUser", id);
            if (null != user) {
//                System.out.println(user.toString());
                return user;
            } else {
//                System.out.println("Not found!");
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            session.rollback();
            return null;
        } finally {
            session.close();
        }
    }

    public static boolean insertUser(String name, String address) {
        SqlSession session = SessionFactory.getSession();

        System.out.println("Inserting user...");
        try {
            User user1 = new User(0, name, address);
            int index = session.insert("UserMapper.insertUser", user1);
            boolean result = (index > 0) ? true : false;
            session.commit();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            session.rollback();
            return false;
        } finally {
            session.close();
        }
    }

    public static boolean updateUser(int id, String name, String address) {
        SqlSession session = SessionFactory.getSession();

        System.out.println("Updating user...");
        try {
            User user = new User(id, name, address);
            int index = session.update("UserMapper.updateUser", user);
            boolean result = (index > 0) ? true : false;
            session.commit();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            session.rollback();
            return false;
        } finally {
            session.close();
        }
    }

    public static boolean deleteUser(int id) {
        SqlSession session = SessionFactory.getSession();

        System.out.println("Deleting user...");
        try {
            int index = session.delete("UserMapper.deleteUser", id);
            boolean result = (index > 0) ? true : false;
            session.commit();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            session.rollback();
            return false;
        } finally {
            session.close();
        }
    }
}

與 Hibernate 每次啟動資料庫操作都得傳入整個 POJO 類/物件不同,Mybatis 的理念是,你給我一串 SQL 和傳入引數就好,我幫你組裝成一條完成的 SQL 拿去 JDBC 執行,Hibernate 則是拿到傳入的這個類/物件,自動拼接其成員屬性生成 SQL。Hibernate 更省事,Mybatis 更靈活(比如需要寫定製化的 SQL)。

結語

本文只簡述 Hibernate 和 Mybatis 兩個 ORM 框架的入門使用。針對二者的 PK ,網上的各家議論見仁見智。只能說一方面要因地制宜,應場景需求挑選框架;另一方面要得心應手,自己用的順手順心,也是挺重要的。

參考文章
Hibernate快速入門
Mybatis 3 簡介