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工作流程
-
通过Reader获取mybatis映射文件。
-
通过SqlSessionFactoryBuilder()构建SqlSessionFactory()工厂对象。
-
获取当前线程SQLSession对象。
-
通过SQLSession读取映射文件中的操作编号,从而读取SQL语句。
-
关闭资源。
完成上面步骤后我们就基本上可以用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(返回值)。
如果文章有错的地方欢迎指正,大家互相交流。
最后,码字不易,喜欢点个赞呀!