阅读 159

Mybatis快速入门指南

搭建mybatis的环境

导入相关jar包

  • mybatis-3.5.3.jar
  • commons-logging-1.1.1.jar
  • log4j-1.2.16.jar
  • cglib-2.2.2.jar
  • asm-3.3.1.jar
  • druid-1.1.9.jar
  • mysql-connector-java-8.0.16.jar

创建mybatis配置文件

<?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="druid.properties" />
	<!--设置一个默认连接环境信息-->
	<environments default="mysql_mybatis">
	<!--连接环境信息,取一个任意唯一的名字-->
	<environment id="mysql_mybatis">
	<!--使用mybatis的事物方式,这里使用jdbc方式-->
    <transactionManager type="jdbc"/>
    <!--使用连接池方式连接-->
    <dataSource type="pooled">
    <!--配置与数据库交互的四个必要属性,注意要跟配置文件里面定义的相同
	在mysql8.0版本以上配置文件要改成这样,否则会出现classNotfound异常
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatistest?useSSL=true&serverTimezone=GMT"/>
        <property name="username" value="root"/>
        <property name="password" value="293911"/>
	-->
        <property name="driver" value="${DriverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    </environments>
</configuration>
复制代码

数据库连接池的配置

其中mybatistest是我数据库的名字,大家可以自行更改。

DriverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatistest?useSSL=true&serverTimezone=GMT
username=root
password=293911
initialSize=5
maxActice=10
maxWait=3000
复制代码

创建mybatis工具类

像之前的DButils一样,用于获取连接,这里获取的是mybatis的SqlSession连接,一般写好后不用怎么改了,通用的。

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.Reader;
import java.sql.Connection;


public class MybatisUtil {
private static ThreadLocal<SqlSession> threadlocal = new ThreadLocal<SqlSession> ();
private static SqlSessionFactory sqlSessionFactory;

static {
    try {
/**
*每 一 个 MyBatis 的 应 用 程 序 都 以 一 个 SqlSessionFactory 对 象 的 实 例 为 核 心 。
SqlSessionFactory 对 象 的 实 例 可 以 通 过 SqlSessionFactoryBuilder 对 象 来 获 得 。
SqlSessionFactoryBuilder 对象可以从 XML 配置文件,
或从 Configuration 类的习惯准备的实例中构建 SqlSessionFactory 对象。	
*/
        //读取xml文件
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        //构建工厂类
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    }
  catch (Exception e){
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}
	
/**
 * 禁止new出来
 */
private MybatisUtil(){}

/**
 * 获取SQLSession
 * @return
 */
public static SqlSession getSqlSession(){
    //从当前线程获取SQLSession对象,可以从当前线程获取也可以从工厂类获取
    SqlSession sqlSession = threadlocal.get();
    if (sqlSession==null){
        sqlSession = sqlSessionFactory.openSession();
        //绑定当前线程
        threadlocal.set(sqlSession);
    }
    return sqlSession;
}
	
/**
 * 关闭连接对象
 */
public static void closeSqlSession(){
    SqlSession sqlSession = threadlocal.get();
    if (sqlSession!=null){
        sqlSession.close();
        //解绑线程目的是让gc快回收。
        threadlocal.remove();
    }
}
	
/**
 * 测试能否获取连接,首先确保数据库已存在!
 * @param args
 */
public static void main(String[] args) {
    Connection connection = MybatisUtil.getSqlSession().getConnection();
    System.out.println(connection!=null?"success":"false");
}
}
复制代码

测试环境是否搭建成功

1. 新建数据库mybatistest
2. 创建students表(id,name,sal)
3. 运行mybatis工具类的主函数
复制代码

数据库sql语句:

CREATE DATABASE mybatisTest; 
CREATE TABLE students( id INT(5) PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(10), sal DOUBLE(8,2) ); 
复制代码

运行结果:

创建实体类与表的映射配置文件

用来写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="np_Student">
<!--
resultMap标签:映射实体与表
type属性:表示实体全路径名
id属性:为实体与表的映射取一个任意的唯一的名字
-->
<resultMap id="StudentMap" type="model.Student">
    <!--
    id标签:映射主键属性
    result标签:映射非主键属性
    property属性:实体的属性名
    column属性:表的字段名
    一般来说表名跟bean类的属性名一样
    id代表数据库表的主键
    -->
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="sal" column="sal"/>
</resultMap>
<!--sql语句写在这里
    在JDBC中我们通常使用?号作为占位符,而在Mybatis中,我们是使用#{}作为占位符
    parameterType我们指定了传入参数的类型
    #{}实际上就是调用了Student属性的get方法
    注意type或者parameterType里面放的都是
    实体类的全路径名,即包名.类名
-->
<insert id="add" parameterType="model.Student">
insert into students values(#{id},#{name},#{sal});
</insert>
</mapper>

复制代码

然后将写好的mapper配置文件添加到一开始写的mybatis配置文件中去,即在mybatis.xml的/configuration标签上面去。

<!--加载实体类与表的映射关系配置-->
<mappers>
    <mapper resource="StudentMapper.xml" />
</mappers>
复制代码

测试向数据库中添加一条数据

1. 创建model包,在model包中新建student类
2. 创建dao包,在dao包中新建add(Student student)方法
复制代码

Student类代码:

    package model;
	public class Student {
	    private Integer id;
	    private String name;
	    private Double sal;
	
	    @Override
	    public String toString() {
	        return "Student{" +
	                "id=" + id +
	                ", name='" + name + '\'' +
	                ", sal=" + sal +
	                '}';
	    }
	
	    public Student() {
	    }
	
	    public Student(Integer id, String name, Double sal) {
	        this.id = id;
	        this.name = name;
	        this.sal = sal;
	    }
	
	    public Integer getId() {
	        return id;
	    }
	
	    public void setId(Integer id) {
	        this.id = id;
	    }
	
	    public String getName() {
	        return name;
	    }
	
	    public void setName(String name) {
	        this.name = name;
	    }
	
	    public Double getSal() {
	        return sal;
	    }
	
	    public void setSal(Double sal) {
	        this.sal = sal;
	    }
	}
复制代码

dao接口类代码:

package dao;
import model.Student;
public interface StudentDao {
public void add(Student student);
}
复制代码

dao实现类代码:

package dao;
import model.Student;
import org.apache.ibatis.session.SqlSession;
import utils.MybatisUtil;

public class StudentDaoImpl implements StudentDao {
    @Override
    public void add(Student student) {
        SqlSession sqlSession = null;
        try {
            //获取sqlSession对象
            sqlSession = MybatisUtil.getSqlSession();
            /**
             * 调用插入方法。
             * insert方法的参数有两个:
             * String s = 命名空间.方法名(id名)
             * Object o = 参数列表。
             * commit方法参数有true/false,默认为true
             * 需要我们手动提交事务。
             */
            sqlSession.insert("np_Student.add",student);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            //事务回滚
            sqlSession.rollback();
            throw e;
        }finally {
            MybatisUtil.closeSqlSession();
        }
    }
}
复制代码

项目结构:

Mybatis工作流程

  1. 通过Reader获取mybatis映射文件。
  2. 通过SqlSessionFactoryBuilder()构建SqlSessionFactory()工厂对象。
  3. 获取当前线程SQLSession对象。
  4. 通过SQLSession读取映射文件中的操作编号,从而读取SQL语句。
  5. 关闭资源。

完成上面步骤后我们就基本上可以用mybatis连接到数据库并会进行一定的操作了,下面我们将使用mybatis进行数据库的增删改查。

使用mybatis对数据库进行增删改查

SQLSession对象:

所有的执行语句的方法,提交或回滚事务都由这个实例来进行操作。

SQLSession对象常用方法:

  • T selectOne(String statement, Object parameter) 只查一个对象,如果为空会抛异常。
  • List selectList(String statement, Object parameter) 查询结果以List集合返回
  • <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
    查询结果以Map集合返回
  • int insert(String statement, Object parameter) 插入
  • int update(String statement, Object parameter) 更新
  • int delete(String statement, Object parameter) 删除
  • void commit(boolean force) 提交事务
  • void rollback(boolean force) 回滚事务

增加数据改进:

由于我们student表id是自增的,所以我们可以不设置id属性,使用mybatis的useGeneratedKeys方法来进行改进添加数据。

<!--对于id主键自增进行自动生成-->
<insert id="add" parameterType="model.Student" useGeneratedKeys="true" keyProperty="id">
    insert into students(name,sal) values(#{name},#{sal});
</insert>
复制代码

查询一条记录:

在StudentMapper.xml中添加select标签,然后在dao类中添加findStuById(int id)接口,
最后在dao实现类中实现findStuById方法;

添加select标签:

<!--resultMap就是上面定义的,可以将查询结果以键值对形式返回-->
 <select id="findStuById" parameterType="int" resultMap="StudentMap">
    select * from students where id = #{id};
 </select>
复制代码

说明一下:MyBatis 中parameter是非常强大的元素,上面的这个示例说明了一个非常简单的命名参数映射。 参数类型被设置为“int” ,因此这个参数可以被设置成任何内容。原生的类型或简单数据类型, 比如整型和没有相关属性的字符串,因此它会完全用参数来替代。对于复杂的参数类型如student类, 它会自动查找相关属性,并将它们的值传递到预处理语句的参数中去。

dao接口类添加:

public Student findStuById(int id);
复制代码

dao实现类添加:

@Override
public Student findStuById(int id) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
    return sqlSession.selectOne("np_Student.findStuById",id);
}catch (Exception e){
    e.printStackTrace();
    throw e;
}finally {
    MybatisUtil.closeSqlSession();
}
}
复制代码

查询所有数据:

在StudentMapper.xml中添加select标签,然后在dao类中添加findAll()接口,最后在dao实现类中通过SQLSession.selectList()方法,实现findAll()方法;

添加select标签:

<select id="findAll" resultMap="StudentMap">
    select * from students;
</select>
复制代码

dao接口类添加:

public List<Student> findAll();
复制代码

dao实现类添加:

@Override
public List<Student> findAll() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
    List<Student> students = sqlSession.selectList("np_Student.findAll");
    return students;
}catch (Exception e){
    e.printStackTrace();
}finally {
    MybatisUtil.closeSqlSession();
}
return null;
}
复制代码

删除数据:

在StudentMapper.xml中添加标签,然后在dao类中添加delStudent(int id)接口,最后在dao实现类中实现delStudent方法;

添加delete标签:

<delete id="delStudent" parameterType="int">
    delete from students where id = #{id};
 </delete>
复制代码

dao接口类添加:

public Student DelStuById(int id);
复制代码

dao实现类添加:

@Override
public Student DelStuById(int id) {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    Student stuById = null;
    try {
		//查询所删除的数据。
        stuById = sqlSession.selectOne("np_Student.findStuById",id);
		//删除对应id的数据。	
        sqlSession.delete("np_Student.delStudent",id);
        sqlSession.commit();
    }catch (Exception e){
        e.printStackTrace();
        sqlSession.rollback();
    }finally {
        MybatisUtil.closeSqlSession();
    }
    return stuById;
 }
复制代码

更新数据:

在StudentMapper.xml中添加标签,然后在dao类中添加updateStudent(Student student)接口,最后在dao实现类中实现updateStudent(Student student)方法;

添加update标签:

<update id="updateStudent" parameterType="model.Student">
	update students set name=#{name},sal=#{sal} where id=#{id};
</update>
复制代码

dao接口类添加:

public void updateStudent(Student student);
复制代码

dao实现类添加:

@Override
public void updateStudent(Student student) {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    try {
        sqlSession.update("np_Student.updateStudent",student);
        //对于增删改操作都需要我们手动提交事务,不然操作是不会成功的。
        sqlSession.commit();
    }catch (Exception e){
        e.printStackTrace();
        sqlSession.rollback();
    }finally {
        MybatisUtil.closeSqlSession();
    }
}
复制代码

mybatis分页:

分页是指查询数据库一定区间范围内的所有数据,sql语句:select * from students limit 0,5;表示从第0行开始,查询5条数据。

添加select标签:

<select id="pagination" parameterType="Map" resultMap="StudentMap">
    <!--根据key自动找到对应Map集合的value-->
    select * from students limit #{start},#{count};
</select>
复制代码

dao接口类添加:

public List<Student> pagination(int start, int count);
复制代码

dao实现类添加:

 @Override
    public List<Student> pagination(int start, int count) {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    try {
        Map<String,Integer> map = new HashMap<>();
        map.put("start",start);
        map.put("count",count);
        //由于参数只能传递一个,所以我们使用map集合,到时候通过key取值。
        List<Student> students = sqlSession.selectList("np_Student.pagination", map);
        return students;
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        MybatisUtil.closeSqlSession();
    }
    return null;
    }
复制代码

动态sql

MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。如果你有使用 JDBC 或其他相似框架的经验, 你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。 动态 SQL 可以彻底处理这种痛苦。 通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形, 这种语言可以被用在任意映射的 SQL 语句中。 动态 SQL 元素和使用 JSTL 或其他相似的基于 XML 的文本处理器相似。

常用标签:

  • if
  • choose(when,otherwise)
  • trim(where,set)
  • foreach

1.if

在动态 SQL 中所做的最通用的事情是包含部分 where 字句的条件。比如: 多条件查询。

用法:

<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
    select * from students
    <where>
    <if test="name!=null">
        and name=#{name};
    </if>
    </where>
 </select>
复制代码

看到这里不知道大家是不是有点疑问呢?我们在JSTL中一般都是用EL表达式如
<c:if test="${name}!=null"}>这样用的,这里怎么直接就写name呢,不是还没有赋值吗?
于是我就想是不是跟map的键有关?为了验证这个想法,我就写了这么一个测试类。

测试标签里test获取的值是map集合的键:

为了更清楚的看出效果,首先将标签里面的"name"改为"mapname"。

<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
    select * from students
    <where>
    <if test="mapname!=null">
        and name=#{mapname};
    </if>
    </where>
 </select>
复制代码

测试方法:

@Test
  public void test() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","lisi");
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    List<Student> students = sqlSession.selectList("np_Student.findByCondition", map);
    System.out.println(students);
}
复制代码

测试结果:

[Student{id=1, name='lisi', sal=11.0}, Student{id=2, name='lee', sal=22.0}, Student{id=3, name='zhangsan', sal=1200.0}]
复制代码

我数据库里面的所有数据都被取了出来,说明了执行findBYCondition方法并没有拼接name="lisi";

将其更改为map.put("mapname",lisi),注意这里的mapname和我们的findBYCondition方法里面定义的 if标签名字对应,如果这次能查询出"lisi"这条记录,说明if标签里面定义的正是参数map的键的名字。

@Test
  public void test() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("mapname","lisi");
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    List<Student> students = sqlSession.selectList("np_Student.findByCondition", map);
    System.out.println(students);
}
复制代码

更改后的测试结果:

[Student{id=1, name='lisi', sal=11.0}]
复制代码

可以证实,标签里面定义的就是map集合的键名。

2. choose(when,otherwise)

跟switch用法差不多。

<select id="choose" parameterType="map" resultMap="StudentMap">
   select * from students
   <where>
       <choose>
           <when test="name!=null">
               and name = #{name}
           </when>
           <otherwise>
               and id = #{id}
           </otherwise>
       </choose>
   </where>
</select>
复制代码

3. trim(where,set)

修剪元素,很强大,我们之所以可以如此方便的使用动态sql,它功不可没。 比如:where 元素知道如果由被包含的标记返回任意内容,就仅仅插入"where" 。 而且,如果以"and "或"or"开头的内容,那么就会跳过 where 不插入。 如果 where 元素没有做出你想要的,你可以使用 trim 元素来自定义。
比如,和 where 元素相等的 trim 元素是:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
</trim>
复制代码

自己定义一个trim标签。

<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
    select * from students
    <!--这里的trim标签就相当于where标签,
    如果where后面是以1=1开头的就将其修剪掉。-->
    <trim prefix="where" prefixOverrides="1=1 ">
    <if test="name!=null">
        1=1 name=#{name};
    </if>
    </trim>
 </select>
复制代码

于where相同的还有set,同样可以用trim标签进行操作。

4. foreach

foreach操作是迭代一个集合, 通常是构建在 in 条件中的。

例子:批量查询

<!--查找一个id集合里面的每个id对应的数据-->
<select id="findStuByIDs" resultMap="StudentMap" parameterType="List">
    select * from students where id in
    <!--collection代表穿进来的集合名称,无论数组还是list集合都是从 "(" 开始 ")" 结束的,分隔符就是","-->
    <foreach collection="list" open="(" close=")" separator="," item="item">
        #{item}
    </foreach>
 </select>
复制代码

进阶

1. 多条件模拟查询:

模糊查询的sql语句:select* from students where name like %name%;

<!--多条件模拟查询-->
    <select id="findByLike" parameterType="map" resultMap="StudentMap">
        select * from students
        <where>
            <if test="name!=null">
                <bind name="newname" value="'%'+name+'%'"/>
                and name like #{newname}
            </if>
            <if test="sal!=null">
                <bind name="newsal" value="'%'+sal+'%'"/>
                and sal like #{newsal}
            </if>
        </where>
    </select>
复制代码

2. 动态更新:

set 元素可以被用于动态包含更新的列,而不包含不需更新的

<!--动态更新-->
 <!--注意不要漏掉逗号,-->
 <update id="updateIfNecessary" parameterType="model.Student">
    update students
    <set>
        <if test="name!=null">
            name=#{name},
        </if>
        <if test="sal!=null">
            sal=#{sal},
        </if>
    </set>
    where id = #{id};
 </update>
复制代码

3. 批量删除:

<!--批量删除-->
<delete id="delStuInList" parameterType="List">
    delete from students where id in
        <foreach collection="list" open="(" close=")" separator="," item="item">
            #{item}
        </foreach>
</delete>
复制代码

4. 动态插入:

说实话,这里我有点懵,在网上看别人动态插入的代码很长很长的一段,又拼接key,又拼接value,是怕担心顺序乱了?
其实我们只要保证顺序一致,就可以不用拼接和判断是否为空了。

<!--动态插入-->
<insert id="dynamicInsert" parameterType="model.Student" useGeneratedKeys="true" keyProperty="id">
    insert into students values
    <!--代表从"("开始,")"结束,如果是已","结尾就把它去掉...
        其实这段不用这么写,我是想熟悉一下这个标签...-->
    <trim prefix="(" suffix=")" suffixOverrides=",">
	<!--这里保证与数据库里的表的顺序一致,不用判断是否为空-->
        #{id},#{name},#{sal}
    </trim>
 </insert>
复制代码

5. 设置别名:

当我们在设置Mapper文件的时候,需要指定parameterType属性或者resultType属性, 该属性值如果不是一般的类型或String类型,比如是对象类型的话,就需要指定全类名, 如果有多个SQL映射语句的话,那么每次都指定全类名的话,可能会比较麻烦,是否有更好的方法, 可以简化一下。你可以通过设置别名的方式来简化。

<!--设置别名-->
<!--放在mybatis配置文件中,放置位置在<configuration>标签下<environments>标签前-->
<typeAliases>
    <!--type是全路径名,alias是设置的别名-->
    <typeAlias type="model.Student" alias="Student"/>
</typeAliases>
复制代码

之后我们再对parameterType属性赋值时可以直接用别名就会方便很多。

<!--动态插入-->
<insert id="dynamicInsert" parameterType="Student" useGeneratedKeys="true"  keyProperty="id">
    insert into students values
    <trim prefix="(" suffix=")" suffixOverrides=",">
        #{id},#{name},#{sal}
    </trim>
 </insert>
复制代码

总结

1. sql写在xml里,便于统一和管理,解除了sql与程序代码的耦合。
2. Mybatis的事务是默认开启的,我们需要手动提交事务。
3. 写Mapper.xml映射文件时,更像在写对应的CURD方法,id(方法名),parameterType(传入参数),resultMap(返回值)。
复制代码

如果文章有错的地方欢迎指正,大家互相交流。 最后,码字不易,喜欢点个赞呀!

关注下面的标签,发现更多相似文章
评论