关于 MyBatis 与 Hibernate 及其 JDBC 的比较, 大家可以参考我的这篇文章: MyBatis+Hibernate+JDBC 对比分析
如果觉得这个还不够系统全面, 可以自行 Google 或者百度.
用了 MyBatis 有两年了, 后来觉得不好用有一定的局限性换成了 MyBatis-Plus, 关于 MyBatis-Plus 实战系列, 可以参考这个链接: https://www.cnblogs.com/youcong/category/1213059.html
另外话说回来了, 为什么我又要回到 MyBatis?
原因有这么几个方面?
第一, MyBatis-Plus 是 MyBatis 的升级版, MyBatis 有的, MyBatis-Plus 都有, MyBatis 没有的, MyBatis-Plus 也有, 从这个角度来看, MyBatis-Plus 是从 MyBatis 中衍生出来的, 那么它们的源码可以说有一大半是一致的, 只是有少部分不同, 所以如果以后 MyBatis-Plus 不能满足我的需求, 我可以自行改造, 虽说码云或者 GitHub 上有不少现成的轮子可以用, 但是我个人觉得, 如果进入一家比较大的公司, 不仅仅要知道如何投机取巧(善用现有的轮子, 同时也要具备制造轮子的能力, 你可以理解为深入了解源码, 并熟悉其中的设计模式);
第二, 如第一条所说的那样, 我看过很多开源项目, 有的理解透彻 MyBatis, 知道 MyBatis 生成的代码有很多局限性, 就例如它的逆向工程可以避免重复编写 CRUD 之类的代码, 但是生成的 xml 太繁琐, 于是就有了 MyBatis-Plus, 但是 MyBatis-Plus 也并不是没有缺点的, 比如它只能针对单表, 而不能多表连接, 也正是因为这个局限性, Jeesite 的创造者才决定自行改造 MyBatis, 让其符合自身的需要, 关于 Jeesite 简化 MyBatis 的博文, 大家可以参考这个链接: https://my.oschina.net/thinkgem/blog/1503611;
第三, 目前主流还是 MyBatis, 互联网项目大多都是 MyBatis, 有的公司也有自己封装的 ORM 框架, 但是本质上基本要么是对 Hibernate 进行改造, 要么就是对 MyBatis 改造, 下面我贴一下关于 MyBatis 的图给大家看看:
从这张图, 大家可以知道 MyBatis 和 MyBatis-Plus 在开源项目都彼此占用;
有人说, 现在博客上有很多关于 MyBatis 的, 你写这个是不是有点多余的, 我对此的回答是, 不多余. 就好比读书, 每个人读一本书一遍或者两遍及其以上, 感触肯定是不一样的. 很多东西当初的时候不知道是什么意思, 回过头来, 你会发现, 你可以发现一些不一样的东西, 这个东西, 你可以理解为举一反三或是融会贯通.
下面进入正题, 关于 MyBatis 的学习, 我当初是参考孤傲苍狼的博客. 今天在写这篇文章时, 我查了官网, 也搜索相关的中文教程, 在 w3cschool 发现了关于 MyBatis, 但是我觉得写的不好, 对于初学者和进阶者而言, 简直就是乱七八糟, 我知道也许这样评价会引起一些人讽刺, 但是这是事实, 大家可以自己看, 我觉得初学者或者进阶者, 特别是进阶者可以回过头再看看官网.
准备环境: Window10/8/7+JDK8/7/6 或以上 + MySQL
项目结构如下:
SQL 脚本:
- REATE TABLE `user` (
- `user_id` int(8) NOT NULL AUTO_INCREMENT COMMENT '用户主键',
- `login_code` varchar(20) NOT NULL COMMENT '用户编码(登录账户) 手机号 邮箱号',
- `user_name` varchar(20) NOT NULL COMMENT '用户名',
- `password` varchar(40) NOT NULL COMMENT '密码',
- `sex` int(2) NOT NULL COMMENT '性别',
- `identity_card` varchar(20) DEFAULT NULL COMMENT '身份证',
- `create_time` datetime NOT NULL COMMENT '创建时间',
- `create_by` varchar(10) NOT NULL COMMENT '创建人',
- `update_time` datetime NOT NULL COMMENT '更新时间',
- `update_by` varchar(10) NOT NULL COMMENT '更新人',
- `status` int(2) NOT NULL DEFAULT '0' COMMENT '状态: 0 注册新用户 1 邮件认证用户 2 管理员 3 黑名单',
- PRIMARY KEY (`user_id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
一, 导入依赖
- <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.youcong.mybatis</groupId>
- <artifactId>mybatis</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <dependencies>
- <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
- <dependency>
- <groupId>org.mybatis</groupId>
- <artifactId>mybatis</artifactId>
- <version>3.4.6</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
- <dependency>
- <groupId>MySQL</groupId>
- <artifactId>MySQL-connector-java</artifactId>
- <version>5.1.38</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/junit/junit -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- </project>
二, 编写实体类
- package com.blog.entity;
- import java.io.Serializable;
- import java.util.Date;
- import java.io.Serializable;
- public class User{
- private static final long serialVersionUID = 1L;
- /**
- * 用户主键
- */
- private Integer userId;
- /**
- * 用户编码(登录账户) 手机号 邮箱号
- */
- private String loginCode;
- /**
- * 用户名
- */
- private String userName;
- /**
- * 密码
- */
- private String password;
- /**
- * 性别
- */
- private Integer sex;
- /**
- * 身份证
- */
- private String identityCard;
- /**
- * 创建时间
- */
- private Date createTime;
- /**
- * 创建人
- */
- private String createBy;
- /**
- * 更新时间
- */
- private Date updateTime;
- /**
- * 更新人
- */
- private String updateBy;
- /**
- * 状态: 0 注册新用户 1 邮件认证用户 2 管理员 3 黑名单
- */
- private Integer status;
- public Date getUpdateTime() {
- return updateTime;
- }
- public void setUpdateTime(Date updateTime) {
- this.updateTime = updateTime;
- }
- public static long getSerialversionuid() {
- return serialVersionUID;
- }
- public Integer getUserId() {
- return userId;
- }
- public void setUserId(Integer userId) {
- this.userId = userId;
- }
- public String getLoginCode() {
- return loginCode;
- }
- public void setLoginCode(String loginCode) {
- this.loginCode = loginCode;
- }
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public Integer getSex() {
- return sex;
- }
- public void setSex(Integer sex) {
- this.sex = sex;
- }
- public String getIdentityCard() {
- return identityCard;
- }
- public void setIdentityCard(String identityCard) {
- this.identityCard = identityCard;
- }
- public Date getCreateTime() {
- return createTime;
- }
- public void setCreateTime(Date createTime) {
- this.createTime = createTime;
- }
- public String getCreateBy() {
- return createBy;
- }
- public void setCreateBy(String createBy) {
- this.createBy = createBy;
- }
- public String getUpdateBy() {
- return updateBy;
- }
- public void setUpdateBy(String updateBy) {
- this.updateBy = updateBy;
- }
- public Integer getStatus() {
- return status;
- }
- public void setStatus(Integer status) {
- this.status = status;
- }
- }
三, 编写数据访问层及其对应的 xml 文件
- package com.blog.dao;
- import org.apache.ibatis.annotations.Result;
- import org.apache.ibatis.annotations.Results;
- import org.apache.ibatis.annotations.Select;
- import com.blog.entity.User;
- public interface UserDao {
- @Select("select * from `user` where user_id=#{userId}")
- @Results(@Result(property="userName",column="user_name"))
- User selectOne(int userId);
- }
@Select 是 MyBatis 的注解写法, 当字段名和属性名一致时, 可以直接 @Select 不需要 @Results,@Results 的作用就是因为实体属性与字段不一致, 为了获取查询结果, 必须要这样.
当然了你也可以不写注解, 直接在对应的 UserDao.xml 写, 如果是 UserDao.xml 里面写的话, 就会变成这样
- package com.blog.dao;
- import org.apache.ibatis.annotations.Result;
- import org.apache.ibatis.annotations.Results;
- import org.apache.ibatis.annotations.Select;
- import com.blog.entity.User;
- public interface UserDao {
- User selectOne(int userId);
- }
需要在 UserDao.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="com.blog.dao.UserDao">
- <!-- 通用查询映射结果 -->
- <resultMap id="BaseResultMap" type="com.blog.entity.User">
- <id column="user_id" property="userId" />
- <result column="login_code" property="loginCode" />
- <result column="user_name" property="userName" />
- <result column="password" property="password" />
- <result column="sex" property="sex" />
- <result column="identity_card" property="identityCard" />
- <result column="create_time" property="createTime" />
- <result column="create_by" property="createBy" />
- <result column="update_time" property="updateTime" />
- <result column="update_by" property="updateBy" />
- <result column="status" property="status" />
- </resultMap>
- <!-- 通用查询结果列 -->
- <sql id="Base_Column_List">
- user_id, login_code, user_name, password, sex, identity_card, create_time, create_by, update_time, update_by, status
- </sql>
- <select id="selectOne" resultMap="BaseResultMap"> select * from `user` where user_id=#{userId} </select> </mapper>
除了 < select > 标签之外, 还有 < insert>,<update>,<delete>, 它们的作用分别是查, 增, 更, 删. 当然了, 还有这里面可以 < sql>, 这个 < sql > 的作用就是为了复用重复列, 省的重复编写代码冗余. 如果要引用, 写成这样既可
- <!-- 通用查询结果列 -->
- <sql id="Base_Column_List">
- user_id, login_code, user_name, password, sex, identity_card, create_time, create_by, update_time, update_by, status
- </sql>
- <select id="selectOne" resultMap="BaseResultMap">
- select <include refid="Base_Column_List"/>
- from `user` where user_id=#{userId}
- </select>
MyBatis 常用的两种写法, xml 方式和注解方式, 但是无论你采用哪种, 对应的 xml 文件必须要存在.
其实 < resultMap id="BaseResultMap"type="com.blog.entity.User"> 可以变成 <resultMap id="BaseResultMap"type="User">
前提必须要在 mybatis-config.xml 配置
- <typeAliases>
- <typeAlias type="com.blog.entity.User" alias="User"/>
- </typeAliases>
但是有人觉得, 如果我的类有很多, 岂不是要配置很多, 太麻烦了, 别怕, MyBatis 已经为你想到了
- <typeAliases>
- <package name="com.blog.entity"/>
- </typeAliases>
这样配置就解决了重复配置的麻烦
其实除了这样, 如果你想标新立异点, 还可以再实体最上面加上 @Alias 注解
这就是 MyBatis 的别名三种方式 / 策略.
四, 编写 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>
- <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://127.0.0.1:3306/blog_test"/>
- <property name="username" value="root"/>
- <property name="password" value="1234"/>
- </dataSource>
- </environment>
- </environments>
- <mappers>
- <mapper resource="mybatis/mapping/UserDao.xml"/>
- </mappers>
- </configuration>
mybatis-config.xml 这个配置没多大用, 坦白说.
因为后期与 Spring 整合由 Spring 来管理数据库连接池对象.
不过还是要稍微讲讲, 以让读者达到开卷有益的目的或者是让没有学过的或者已经学过但是不知道的涨涨见识.
- <environment id="development">
- 表示我们默认使用 development 配置环境
- <transactionManager type="JDBC" />
- 采用 JDBC 的事务管理模式
- <dataSource type="POOLED">
- 数据库连接信息
- <mappers>
- 映射器 通常一般映射 xml 文件
五, 编写工具类
- package com.blog.config;
- import java.io.IOException;
- import java.io.InputStream;
- import org.apache.ibatis.io.Resources;
- import org.apache.ibatis.session.SqlSessionFactory;
- import org.apache.ibatis.session.SqlSessionFactoryBuilder;
- public class SqlSessionFactoryUtils {
- private static SqlSessionFactory sqlSessionFactory = null;
- public static SqlSessionFactory getSqlSessionFactory() {
- InputStream is = null;
- if(sqlSessionFactory==null) {
- String resource = "mybatis/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;
- }
- }
说到上述代码, 顺便谈谈 MyBatis 的核心组件. 它的核心组件主要有这么几个, 如下所示:
SqlSessionFactoryBuilder: 它会根据配置信息或者代码来生成 SqlSessionFactory;
SqlSessionFactory: 依赖工厂生成 SqlSession;
SqlSession: 是一个即可发送 SQL 执行返回结果, 也可以获取 Mapper 接口;
Sql Mapper: 它是 MyBatis 的新设计组件, 它是由一个 Java 接口和 xml 文件 (或注解) 构建, 需要给出对应的 SQL 和映射规则. 它负责发送 SQL 去执行并返回结果.
用一张图来表示它们之间的关系, 如图:
它们的生命周期在此稍微说一下:
(1)SqlSessionFacotoryBuilder
SqlSessionFacotoryBuilder 是利用 xml 和 Java 代码获得资源来构建 SqlSessionFactory 的, 通过它可以构建多个 SqlSessionFactory, 它的作用就是一个构建器, 一旦我们构建了 SqlSessionFactory, 它的作用就完结, 它就失去存在的意义. 这时我们应该毫不犹豫的废弃它, 将它回收. 所以它的生命周期只存在方法局部, 它的作用就是生成 SqlSessionFactory.
(2)SqlSessionFactory 的作用是创建 SqlSession, 而 SqlSession 就是一个会话, 相当于 JDBC 中的 Connection 对象. 每次应用程序需要访问数据库, 我们就要通过 SqlSessionFactory 创建 SqlSession, 所以 SqlSessionFactory 应该在 MyBatis 应用的整个生命周期中. 而如果我们多次创建同一个数据库的 SqlSessionFactory, 则每次创建 SqlSessionFactory 会打开更多的数据库连接资源, 那么连接资源就很快会被耗尽. 因此 SqlSessionFactory 的责任是唯一的, 它的责任就是创建 SqlSession, 所以我们果断采用单例模式. 如果采用多例, 那么它对数据库连接的消耗是很大的, 不利于我们统一管理. 所以正确的做法是使得每个数据库只对应一个 SqlSessionFactory, 管理好数据库资源分配, 避免过多的 Connection 被消耗.
(3)SqlSession
SqlSession 是一个会话, 相当于 JDBC 的一个 Connection 对象, 它的生命周期应该是在请求数据库处理事务的过程中. 它是一个线程不安全的对象, 在涉及多线程的时候我们需要特别当心, 操作数据库需要注意其隔离级别, 数据库锁等高级特性. 此外, 每次创建的 SqlSession 都必须及时关闭它, 它长期存在就会使数据库连接池的活动资源减少, 对系统性能的影响很大.
(4)Mapper
Mapper 是一个接口, 而没有任何实现类, 它的作用是发送 SQL, 然后返回我们需要的结果, 或者执行 SQL 从而修改数据库的数据, 因此它应该在一个 SqlSession 事务方法之内, 是一个方法级别的东西. 它就如同 JDBC 中的一条 SQL 语句的执行, 它的最大范围和 SqlSession 是相同的. 尽管我们想一直保存着 Mapper, 但是你会发现它很难控制, 所以尽量在一个 SqlSession 事务的方法中使用它们, 然后废弃掉.
MyBatis 组件生命周期, 如图:
六, 测试
- package mybatis;
- import org.apache.ibatis.session.SqlSession;
- import org.junit.Test;
- import com.blog.config.SqlSessionFactoryUtils;
- import com.blog.dao.UserDao;
- import com.blog.entity.User;
- public class MyBatisTest {
- @Test
- public void testName() throws Exception {
- SqlSession sqlSession = null;
- sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
- UserDao userDao = sqlSession.getMapper(UserDao.class);
- User user = userDao.selectOne(1);
- System.out.println(user.getUserName());
- }
- }
小结:
通过这篇文章, 你可以达到能够知道怎么使用 MyBatis 和了解熟悉它的相关执行原理和对应的生命周期, 希望能给大家带来有益的启发和收获.
本文参考:
《深入浅出 MyBatis 技术原理和实战》
MyBatis 官网: http://www.mybatis.org/mybatis-3/
来源: https://www.cnblogs.com/youcong/p/9975973.html