1. mybatis基础环境搭建
若想使用mybatis,需要有如下两个jar包:①mybatis的核心jar包。②数据库驱动包。
(想使用别人提供的服务就必须要有别人的jar包;mybatis是和数据库打交道的,那么你的程序中,数据库的驱动包也是必不可少的。)
Maven依赖如下:
<!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency><!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency>
2. mybatis核心配置文件
在导入了上面两个jar包之后,我们需要为程序提供必要的信息才能使mybatis正常执行,以及如果我们需要使用mybatis提供的一些功能也需要进行相关的配置,而这些信息就写在核心配置文件中。
mybatis的配置文件为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="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="数据库url"/>
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射文件 -->
<mappers>
<mapper resource="mappers"></mapper>
</mappers>
</configuration>
这是mybatis最基本的配置信息,其中包含:①事务处理方式。②数据源。③映射文件位置。
对于要使用mybatis的程序,一定要考虑如下问题:
①事务是怎么处理的。②数据源是什么。③映射文件在哪。
3. mapper和映射文件
mapper是java接口,就是DAO,不过我们不需要提供实现类,mybatis会在程序执行时根据映射文件的内容来为mapper接口生成动态代理类,这些动态代理类的方法就是我们在接口中的方法的实现。
mapper接口:
public interface UserMapper {
public int create(String name);
public int remove(int id);
}
映射文件:
<?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接口全类名一致 -->
<mapper namespace="com.test.mappers.UserMapper">
<!-- SQL标签id和接口的方法名一致 -->
<!-- public int create(String name);-->
<insert id="create">
insert into `user` (`name`) values (#{name,jdbcType=VARCHAR})
</insert>
<!-- public int remove(int id);-->
<delete id="remove">
delete from `user` where `id`=#{id,jdbcType=INTEGER}
</delete>
</mapper>
这里我们要问:为什么我们不用写接口的实现却可以在程序中直接使用mapper接口的方法?mybatis怎么知道我们的方法要实现什么功能?
显然,如果没有任何的信息,mybatis也是不可能知道我们的mapper接口的方法要实现什么功能,所以我们需要给mybatis提供相应的信息,告诉mybatis我们需要实现什么功能。那我们要怎么告诉mybatis呢?核心配置文件是用来写主要的配置信息的,而我们的mapper接口会有很多,显然如果把与接口有关的所有信息都写在核心配置文件中不太合适。但是,我们在配置文件中可以看到有映射文件位置这一配置,那么我们就可以把与接口有关的信息写在映射文件中,mybatis在为接口创建动态代理类时只需要到映射文件中找相关的信息就可以了。这样既把接口相关的信息告诉了mybatis又不会使核心配置文件变的臃肿。而事实上映射文件的作用也就是这样。
解决了怎么把信息传递给mybatis,接下来是我们要传递什么信息给mybatis呢?
首先,我们需要告诉mybatis,哪个映射文件保存了哪个mapper接口的信息;其次,我们需要告诉mybatis我们的接口的方法要实现什么功能。
这就有了以下两个规则:
①映射文件的命名空间和mapper接口的全类名必须保持一致。
(用来表示哪个映射文件保存了哪个mapper接口的信息。)
②mapper接口的方法名和映射文件中编写SQL的标签的id属性保存一致。
(用来表示编写的SQL标签写的是哪个方法的相关信息。)
4.使用mybatis创建的Mapper接口动态代理类
总体流程如下:
代码如下:
public class Main {
public static void main(String[] args) throws IOException {
// 获取核心配置文件的输入流
InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
// 创建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder=new SqlSessionFactoryBuilder();
// 获取SqlSessionFactory
SqlSessionFactory sqlSessionFactory=sqlSessionFactoryBuilder.build(is);
// 获取SqlSession,这里传入的true和false是用来表示是否自动提交
SqlSession sqlSession=sqlSessionFactory.openSession(true);
// 获取XxxMapper接口的动态代理类
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
// 调用相关方法
userMapper.create("张三");
userMapper.create("李四");
userMapper.remove(2);
}
}
5. mybatis获取参数
我们在开发中经常需要使用不同的参数来执行同样的代码,如插入数据时每次的代码相同只是参数的数据不同,那么我们要怎么把这些参数设置到我们的SQL语句中呢?
我们的参数只能靠接口方法的形参传入进来,传进来以后,mybatis提供了两种方式来获取形参中的值:①${}。②#{}。我们可以直接在映射文件中使用上面两个表达式来获取参数的值并将参数的值设置到SQL语句中,实现参数的传递与获取。
这两种方式的区别在于:#{}相当于占位符,它在把数据设置到SQL语句中时会为数据加上单引号,而${}相当于字符串拼接,它在把数据设置到SQL语句中时不会为数据加上单引号。
根据接口方法形参的形式不同我们有不同的获取方式:
①如果接口方法形参只有一个参数,我们可以直接#{}或${}中间加任意值来获取传入的参数。
<!-- public void updateName(String name) -->
<update id="updateName">
update `user` set `name` = #{newName} where `name`="李四"
</update>
②如果接口方法形参是实体类型或map集合,可以使用#{属性名或键名}来获取(${}同理)。
<!-- public void delete(User user) -->
<delete id="delete">
delete from `user` where `name`=#{userName} and `id`=#{userId}
</delete>
③如果接口方法有多个形参,这些形参会被放入到一个map集合中,以arg0,arg1...或param1,param2...为键,参数值为值,则转换为②的情况获取参数的值。
<!-- public void delete(String name,Int id) -->
<delete id="delete">
delete from `user` where `name`=#{arg0} and `id`=#{arg1}
</delete>
④如果在接口方法的参数上加了@param注解,则我们要用注解中标记的内容为键才能获取参数的值。
<!-- public void delete(@param(value="myName")String name,
@param(value="id")Int myId) -->
<delete id="delete">
delete from `user` where `name`=#{myName} and `id`=#{myId}
</delete>
如果传入的参数是一个实体类对象,我们获取参数值的方式是:#{属性名},这里不是调用方法,但是我们可能是把属性声明为private的,这样不禁问:怎么可以直接使用属性名来获取属性值呢? 所以,我们需要在实体类中为对应的属性声明get()/set()方法。
6. mybatis的查询
select标签需要设置resultType或resultMap来设置查询的返回值的映射情况。
使用resultType会按照设置的类型的属性来和查询结果自动匹配。使用resultMap会按照自己设置的匹配规则来匹配。
<select id="select" resultMap="all">
select * from `regist_email`
<where>
<if test="id!=null">
and `id`=#{id}
</if>
<if test="email!=null">
and `email`=#{email}
</if>
<if test="code!=null">
and `code`=#{code}
</if>
<if test="createTime!=null">
and `create_time`=#{createTime}
</if>
</where>
</select>
7. resultMap
resultMap标签的id用来设置当前resultMap标签的唯一标识,type用来设置该resultMap表示的查询结果要映射的实体类。
result子标签用来设置映射关系,property表示实体类的属性,column表示查询结果的一列。
id子标签用来设置主键的映射关系,property表示实体类的属性,column表示查询结果的一列。
association子标签用来设置多对一的关系,其有两种方法:①使用该标签的property表示实体类的属性名,JavaType表示该属性对应的java类,之后用association标签的result和id子标签来继续设置映射关系。②使用association标签的property来表示实体类的属性名,select来设置查询这个属性值的SQL语句和使用column来设置传入给SQL语句的参数。
collection子标签用来设置一对多的关系,其有两种方法:①使用该标签的property表示要设置的属性,ofType来设置该属性的个体的类型,之后再使用result和id子标签来设置其他对应关系。②使用该标签的property来表示要设置的属性,select来设置查询语句,column来设置要传入的数据。
这里需要注意的是:MySQL中的DateTime数据类型在java中使用Timestamp类来接收。
<resultMap id="all" type="com.coward.entity.RegistEmail">
<id property="id" column="id"></id>
<result property="email" column="email"></result>
<result property="code" column="code"></result>
<result property="createTime" column="create_time"></result>
</resultMap>
8. 查询结果的返回
查询结果可分两部分:①单条记录的封装。②整个查询结果的封装。
单条记录的封装可以直接封装到实体类中,只需要在select标签的resultType上设置相对应的映射就可以。也可以封装到一个map中,以列为键,查询结果为值,这时候把resultType设置成map即可。
整个查询结果,只需要设置方法的返回值即可进行对应的封装。如果是只有一条记录可以直接返回封装的类型。如果是多条记录,可以返回list类型或map类型,mybatis会把一条条封装后的记录放入相对应的list或map中。如果是map则需要在接口方法上使用@MapKey来设置作为键值的属性。
因为在封装查询结果为对象的过程中需要创建对象,所以要求在实体类中有对应的构造方法用于创建对象。同时对象创建过程中调用的构造方法的形参顺序和查询结果一一对应,所以哪怕数据全部提供也可能会出现形参和实参位置不匹配的情况,所以要写好查询语句和构造方法。
9. 动态SQL
if标签:test属性为true时执行标签中的内容。
where标签:结合if标签使用,如果where内部的if标签的test全为false则不生效;如果不全为false则会在标签内容前面加上where,并且会删掉where后面紧跟的and。
<select id="select" resultMap="all">
select * from `regist_email`
<where>
<if test="id!=null">
and `id`=#{id}
</if>
<if test="email!=null">
and `email`=#{email}
</if>
<if test="code!=null">
and `code`=#{code}
</if>
<if test="createTime!=null">
and `create_time`=#{createTime}
</if>
</where>
</select>
choose、when、otherwise: 类似于if...else if...else。
trim:格式化,prefix属性用来为标签体内容加上前置;prefixOverrides属性用来为标签体内容删除指定前缀;suffic属性用来为标签题内容加上后缀;suffixOverrides属性用来为标签贴内容删除指定后缀。
<insert id="insert">
INSERT INTO `regist_email` (`id`,`email`,`code`,`create_time`)
VALUE
<trim prefix="(" suffix=")">
<if test="id==null">
null,<include refid="just3"></include>
</if>
<if test="id!=null">
#{id},<include refid="just3"></include>
</if>
</trim>
</insert>
foreach:collection设置要循环的数组或集合。item设置每次循环的一个数据。separator设置循环体之间的分隔符。open设置开始符。close设置结束符。
注意:在标签的test等属性中,获取参数值只需要直接写参数名即可,不需要写#{}。