Hibernate 與 Mybatis 入門使用
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&useUnicode=true&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):
ORM 配置檔案 ClassName.hbm.xml :
Hibernate 作為一個 ORM (Object Relational Mapping – 物件–關係型資料庫 對映),它使用 ClassName.hbm.xml 配置檔案來作為 資料庫表 和 Java類的對映配置,本文以兩個簡單的資料表 (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&useUnicode=true&characterEncoding=UTF-8
JDBC.userName=root
JDBC.userPassword=123456
本文的測試工程結構如下(使用 Maven 工程形式):
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 簡介