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(返回值)。

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

posted @ 2020-05-16 13:40  yoleen  阅读(429)  评论(1编辑  收藏  举报