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

如果文章有错的地方欢迎指正,大家互相交流。

最后,码字不易,喜欢点个赞呀!

最新文章

  1. Windows Git安装指南
  2. Leetcode Find Minimum in Rotated Sorted Array I and II
  3. UE4 自定义物理表面类型(Surface Type)
  4. Git忽略配置文件gitignore
  5. P6 Enterprise Project Portfolio Contract Management
  6. Linux最常用命令及快捷键整理
  7. 制作自己的ros机器人(navigaion)前提--22
  8. C++ const使用详解
  9. TreeView节点
  10. 改进的SMO算法
  11. 使用XRDP实现Windows远程桌面Linux系统
  12. GridView点击行,选中模版列中CheckBox
  13. Matrix Swapping II(求矩阵最大面积,dp)
  14. Payload Inject And Fake
  15. &lt;转&gt;SQL的执行顺序
  16. 「POJ - 2318」TOYS (叉乘)
  17. 键盘控制div移动并且解决停顿问题(原生js)
  18. 使用python遍历文件夹取出特定的字符串
  19. delphi如何检索adoquery里面某一列存在的重复行?
  20. 使用rem的原理,62.5%,根据屏幕宽度等比压缩网页

热门文章

  1. tensorflow1.0 模型的保存与加载
  2. linux下文件的打包和压缩
  3. tp5--路由的使用(初级)
  4. beego rel/reverse
  5. 2019-2020-1 20199310《Linux内核原理与分析》第四周作业
  6. MarkDown排版测试
  7. SSL/TLS 漏洞“受戒礼”,RC4算法关闭
  8. c++库 c语言接口
  9. Linux网络服务第七章DNS域名解析服务
  10. CYQ.Data 轻量数据层之路 使用篇-MProc 存储过程与SQL 视频[最后一集] H (二十八)