编辑
2019-02-03
服务端技术
00
请注意,本文编写于 1694 天前,最后修改于 105 天前,其中某些信息可能已经过时。

目录

MyBatis简介
从HelloWorld开始
MyBatis的开发模式
1、配置文件
2、接口
3、接口+配置文件
MyBatis的CURD
MyBatis的传参
1、出错演示
2、顺序传参法
3、注解传参法
4、Map传参法
5、JavaBean传参法
MyBatis动态SQL
if
where+if
trim
set
choose
foreach

MyBatis简介

首先说一下MyBatis是什么?MyBatis就是下图中的鸟,哈哈! mark MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设首参数以及获取结果集。MyBatis可以使用简单的XML或注解来配罝和映射原生信息,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

MyBatis和Hibernate一样,是一个ROM框架(Object Relational Mapping,是对象到关系的映射,是一种解决实体对象与关系型数据库相互匹配的技术,它的实现思想就是将数据库中数据表映射成为对象,对关系型数据以对象的形式进行操作。在软件开发中,对象和关系数据是业务实体的两种表现形式,ORM通过使用描述对象和数据库之间映射的元数据,将对象自动持久化到关系数据库中。

MyBatis相对于Hibernate来说更加轻量级,所以MyBatis其实不具备像Hibernate那样自动建表的功能,但是MyBatis现在对我来说足够用了,现在开始记录一下学习MyBatis过程中遇到的问题或者MyBatis的知识点!

从HelloWorld开始

下面从MyBatis的helloworld说起,如何开始学习MyBatis: 首先是MyBatis的官方网站,这里具有最齐全的MyBatis文档,关键是居然有中文版的!?!?http://www.mybatis.org/mybatis-3/zh/index.html 另外,这个文档有PDF版本的,可以在网上下载到,如果你希望使用离线版的文档去下载这个PDF即可!

这个是MyBatis的maven依赖,如果没学过maven也不要紧,可以参考我的一片博客《一文读懂Maven》,题目有点夸张,大家将就着看吧!如果你不希望使用maven的话直接导入jar包就好了,MyBatis只有一个jar包(但是不要忘记mysql驱动程序的jar包)!

xml
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>x.x.x</version> </dependency>

我就演示一下非maven版本的HelloWorld,首先准备一个数据库和表,如图: mark 这是我整个工程的目录结构: mark resource.xml

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/mybatis" /> <property name="username" value="root" /> <property name="password" value="1234" /> </dataSource> </environment> </environments> <mappers> <mapper resource="com/xpu/bean/Employee.xml" /> </mappers> </configuration>

dataSource 标签配置的数据库连接的基本信息,mappers标签配置的是对象映射关系文件,说一下关于引入dtd约束文档的问题,如果未配置dtd约束文档,那么eclipse是没有提示的,你可以在Window--->Preference-->XML--->XMLCatalog里面配置dtd,点击Add,选择URL,填入dtd的URL,选择FileSystem点击打开下载好的dtd文件即可! mark 接着创建一个JavaBean:

java
package com.xpu.bean; public class Employee { private Integer id; private String name; private char gender; private String address; public Employee() { super(); } public Employee(Integer id, String name, char gender, String address) { super(); this.id = id; this.name = name; this.gender = gender; this.address = address; } public Integer getId() {...} public void setId(Integer id) {...} public String getName() {...} public void setName(String name) {...} public char getGender() {...} public void setGender(char gender) {...} public String getAddress() {...} public void setAddress(String address) {...} @Override public String toString() {...} }

创建关系映射文件employee.xml,注意namespace必须和JavaBean一一对应,sql查询语句不要写错,id是要传入的一个参数:

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.xpu.bean.Employee"> <select id="selOne" parameterType="java.lang.String" resultType="com.xpu.bean.Employee"> select * from employee where id = #{id} </select> </mapper>

Test.java

java
package com.xpu.bean; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class Test { public static void main(String[] args) throws IOException { //通过resource.xml中的配置信息获取一个SqlSessionFactory对象 Reader reader = Resources.getResourceAsReader("resource.xml"); SqlSessionFactory sqlSessionFty = new SqlSessionFactoryBuilder().build(reader); //开始一个会话 SqlSession sqlSession = sqlSessionFty.openSession(); //执行查询方法 Employee one = sqlSession.selectOne("com.xpu.bean.Employee.selOne", "1"); System.out.println(one); //关闭会话 sqlSession.close(); } }

mark 这便查询到了ID为1的用户,HelloWorld的演示到此结束!

MyBatis的开发模式

1、配置文件

resource.xml

xml
<mappers> <mapper resource="com/xpu/bean/Employee.xml" /> </mappers>

Employee.xml

xml
<mapper namespace="com.xpu.bean.Employee"> <select id="selOne" parameterType="java.lang.String" resultType="com.xpu.bean.Employee"> select * from employee where id = #{id} </select> </mapper>

这样的方式就是我的HelloWorld中所用到的方式,Employee.xml的namespace是我的JavaBean,执行查询的时候需要使用如下方式,selOne是select标签的id,parameterType代表参数的类型,resultType代表返回值的类型,中间是sql查询语句!

java
//执行查询方法 Employee one = sqlSession.selectOne("com.xpu.bean.Employee.selOne", "1");

2、接口

IEmployeeDao.java

java
public interface IEmployeeDao { //查询所有记录 @Select("select * from employee") public List<Employee> getAll(); }

resource.xml

xml
<mappers> <mapper class="com.xpu.dao.IEmployeeDao"/> </mappers>

使用该模式测试:

java
package com.xpu.dao; import java.io.IOException; import java.io.Reader; import java.util.List; 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 com.xpu.bean.Employee; public class Test { public static void main(String[] args) throws IOException { Reader reader = Resources.getResourceAsReader("resource.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); //true表示是否自动提交,默认值是false SqlSession session = factory.openSession(true); IEmployeeDao mapper = session.getMapper(IEmployeeDao.class); List<Employee> all = mapper.getAll(); for (Employee employee : all) { System.out.println(employee); } } }

3、接口+配置文件

这种模式是最常用的模式: Employee.xml:

xml
<mapper namespace="com.xpu.dao.IEmployeeDao"> <select id="getAll" resultType="com.xpu.bean.Employee"> select * from employee </select> <select id="getOne" resultType="com.xpu.bean.Employee"> select * from employee where id = #{id} </select> </mapper>

resource.xml

xml
<mappers> <mapper resource="com/xpu/bean/Employee.xml" /> </mappers>

IEmployeeDao.java

java
package com.xpu.dao; public interface IEmployeeDao { public List<Employee> getAll(); public Employee getOne(String id); }

以上就是MyBatis的三种开发模式,第三种用的比较多,第一种局限性较大,第二种耦合度太强,开发本来的原则就是高内聚、低耦合,使用第二种方式反倒是的耦合度增强不宜使用,所以常用的方式就是第三种开发模式!

下面对需要注意的地方总结一下,参数类型的配置要写全路径名称,避免出错,使用第二种开发模式的时候,需要将接口配置成class,定义返回值类型的时候注意,虽然getAll返回的是一个List集合,但是集合中装的还是对象,所以我们在配置返回值的时候还是需要配置成Employee类:

xml
<mapper class="com.xpu.dao.IEmployeeDao"/>

最后说一个比较重要的问题,为什么我们通过IEmployeeDao.class对象得到的mapper对象是一个实例呢?我们并没有实现接口呀?没有实现接口的话为什么可以拿到他的实现类对象呢?

这是其实是个代理对象,是通过动态代理来实现的,具体参考Proxy类,看看如何通过动态代理生成代理对象,具体可以参考《Mybatis是如何通过mapper接口生成代理对象的》

MyBatis的CURD

MyBatis最核心的作用还是增删改查嘛,接下来我通过第三种开发模式总结一下MyBatis的CURD,看看Employee.xml中的SQL语句:

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.xpu.dao.EmployeeDao"> <select id="selAll" resultType="com.xpu.bean.Employee"> select * from employee </select> <select id="selOne" parameterType="java.lang.String" resultType="com.xpu.bean.Employee"> select * from employee where id = #{id} </select> <insert id="insertEmp" parameterType="com.xpu.bean.Employee"> insert into employee values(#{id}, #{name}, #{gender}, #{address}) </insert> <update id="updateEmp" parameterType="com.xpu.bean.Employee"> update employee set name = #{name}, address = #{address} where id = #{id} </update> <delete id="daleteEmp"> delete from employee where id = #{id} </delete> </mapper>

IEmployeeDao.java

java
public interface EmployeeDao { //查询所有 public List<Employee> selAll(); //查询单一 public Employee selOne(String id); //新增 public void insertEmp(Employee emp); //修改 public void updateEmp(Employee emp); //删除 public void daleteEmp(String id); }

测试代码:

java
public class Demo { public static void main(String[] args) throws IOException{ Reader reader = Resources.getResourceAsReader("resource.xml"); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession session = sessionFactory.openSession(); IEmployeeDao mapper = session.getMapper(IEmployeeDao.class); mapper.insertEmp(new Employee(5, "Lucy", '1', "Lucy@163.com")); mapper.daleteEmp("4"); mapper.updateEmp(new Employee(5, "Lucy", '1', "Lucy@qq.com")); System.out.println(mapper.selOne("2")); session.commit(); session.close(); } }

MyBatis的传参

1、出错演示

好了,增删查改的基本功能已经演示完毕,但是假设我们需要做一个多条件查询的方法,则需要这样写(一下代码均为省略代码,只贴上了重要的代码,其他的代码均在CURD的演示中): Employee.xml

xml
<select id="queryList" resultType="com.xpu.bean.Employee"> select * from employee where name like '#{name}%' and gender = #{gender} </select>

IEmployeeDao.java

java
//多条件查询 public List<Employee> queryList(String name, char gender);

多条件查询测试:

java
public void test() { IEmployeeDao mapper = session.getMapper(IEmployeeDao.class); List<Employee> list = mapper.queryList("t", '1'); for (Employee employee : list) { System.out.println(employee); } }

但是却报错了,注意红线标记的地方,我们需要修改参数的传递方式: mark

2、顺序传参法

其实对于这种多参数的情况,应该这样写Employee.xml,这叫做顺序传参法:

xml
<select id="queryList" resultType="com.xpu.bean.Employee"> select * from employee where name like '${param1}%' and gender = #{param2} select * from employee where name = #{0} and gender = #{1} </select>

#{ }里面的数字代表你传入参数的顺序,SQL语句的语义表达不直观,且一旦顺序调整容易出错,所以不建议使用!

3、注解传参法

但是这样写的可读性太差,可以使用注解的方式:

xml
<select id="queryList" resultType="com.xpu.bean.Employee"> select * from employee where name like '${name}%' and gender = #{gender} </select>
java
//多条件查询 public List<Employee> queryList(@Param("name") String name, @Param("gender") char gender);

这样可读性强,而且不会因为参数的顺序问题导致程序异常!

4、Map传参法

Employee.xml

xml
<select id="queryList" resultType="com.xpu.bean.Employee"> select * from employee where name like '${name}%' and gender = #{gender} <select>

IEmployeeDao.java

java
public List<Employee> queryList(Map map);

测试代码:

java
public void test() { IEmployeeDao mapper = session.getMapper(IEmployeeDao.class); Map map = new HashMap(); map.put("name", "t"); map.put("gender", '1'); List<Employee> list = mapper.queryList(map); }

5、JavaBean传参法

这个在MyBatis的CURD中已经演示过了,resultType="com.xpu.bean.Employee" ,通过#{ 对象属性 } 便可以传参,在此不再赘述!

MyBatis动态SQL

MyBatis 的强大特性之一便是它的动态 SQL。你可以在这个网站学习动态SQL《动态 SQL》 ,如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

if

就是简单的条件判断,利用if语句我们可以实现某些简单的条件选择

xml
<!-- 动态SQL --> <select id="queryByIf" resultType="com.xpu.bean.Employee"> select * from employee where 1=1 <if test="name != null"> and name like '${name}%' </if> </select>

但是在上面的例子中,如果name为空的话,where后面就什么都没有,所以加上1=1完全是为了保证语法的正确性,这个问题将在下面解决!

where+if

where标签知道如果它包含的标签中有返回值的话,它就插入一个where。此外,如果标签返回的内容是以AND 或 OR开头的,则它会剔除掉。

xml
<select id="queryByWhere" resultType="com.xpu.bean.Employee"> select * from employee <where> <if test="name != null"> name like '${name}%' </if> <if test="address != null"> and address = #{address} </if> </where> </select>

上述例子中,如果name不为空,address不为空,则SQL语句是select * from employee where name like '${name}%' and address = #{address} ,即使name为空,address前面的and也会被去掉,同样是合法的SQL语句,如果都为空,那就变成了查询所有记录,同样也是合法的SQL语句!

trim

trim标签是可以让我们自己去实现一个where标签,或者实现更多的功能,下面用trim标签去实现一个where标签:

xml
<select id="queryByTrim" resultType="com.xpu.bean.Employee"> select * from employee <trim prefix="where" prefixOverrides="AND | OR"> <if test="name != null"> name like '${name}%' </if> <if test="address != null"> and address = #{address} </if> </trim> </select>

这个trim标签其实就是一个可以通过自定义属性去完成一些功能的标签,prefix="where" 就是代表如果标签中有内容则加上where,如果where后面直接遇到AND或者OR这样的标签就会将其剔除!

set

xml
<update id="updateEmployee" parameterType="com.xpu.bean.Employee"> update user <set> <if test="name!=null"> name=#{name} </if> </set> <where> <if test="address!=null"> address=#{address} </if> </where> </update>

set标签代替了sql中set关键字,set标签可以自动去除sql中的多余的',',这个和where标签是一样的道理,同样的我们可以使用trim来实现同样的功能:

xml
<update id="updateEmployee" parameterType="com.xpu.bean.Employee"> update user <trim prefix="set" prefixOverrides=","> <if test="name!=null"> name=#{name} </if> </trim> <where> <if test="address!=null"> address=#{address} </if> </where> </update>

choose

chose标签类似于流程控制中switch case default语句,配合when、otherwise标签使用:

xml
<select id="queryByChoose" resultType="com.xpu.bean.Employee"> select * from employee where 1=1 <choose> <when test="name != null"> and name like '${name}%' </when> <when test="address != null"> and address = ${address} </when> <otherwise> order by name </otherwise> </choose> </select>

foreach

foreach标签可迭代任何对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数,下面是一个根据ID批量删除的示例:

xml
<delete id="deleteByList"> delete from employee where id in <foreach collection="list" open="(" separator="," close=")" item="myitem"> #{myitem} </foreach> </delete> <delete id="deleteByArray"> delete from employee where id in <foreach collection="array" open="(" separator="," close=")" item="myitem"> #{myitem} </foreach> </delete>
java
//批量删除 public Integer deleteByList(List<String> list); //批量删除 public Integer deleteByArray(String[] strs);
java
public void func() throws IOException { Integer array = mapper.deleteByArray(new String[] {"1", "2"}); System.out.println("影响行数:" + array); List<String> list = new ArrayList<>(); list.add("3"); list.add("4"); Integer deleteByList = mapper.deleteByList(list); System.out.println("影响行数:" + deleteByList); session.commit(); session.close(); }

动态 SQL 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 SQL 语句出来,然后在通过 MyBatis 动态SQL 对照着改,防止出错!

本文作者:Tim

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!