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: langlv@qq.com
* @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 简介