前言
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
官方文档: https://mybatis.org/mybatis-3/zh/getting-started.html
框架架构
基本使用
全局配置(mybatis-config.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>
<!-- 可以引用一个外部的properties文件 -->
<properties resource="mybatis/config.properties">
<!-- 也可以单独定义属性 -->
<!--<property name="username" value="root"/>-->
<!--<property name="password" value="Xiele"/>-->
</properties>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<!-- 每个类都要配置一个别名 -->
<!--<typeAlias alias="User" type="com.example.start.springdemo.mybatis.User" />-->
<package name="com.example.start.springdemo.mybatis"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mybatis/mapper/UserMapper.xml"/>
</mappers>
</configuration>
注: 配置文件的元素需要保证顺序,严格按照如下顺序:
configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?
定义别名可以针对每个类作定义,也可以直接扫描包,默认别名为小写的类目,但mybatis不区分大小写,在resultType,或parameterType可以指定大小的短类目或小写的类目。因此可建议直接扫描包来定义别名。
官方文档描述如下: 每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子: @Alias(“author”) public class Author { … }
Mapper文件
<?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.example.start.springdemo.mybatis.dao.UserMapper">
<resultMap id="UserMap" type="com.example.start.springdemo.mybatis.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="selectUser" resultMap="UserMap">
select * from spring_user where id = #{id}
</select>
<select id="selectUserByType" resultType="User">
select * from spring_user where id = #{id}
</select>
<select id="listUsers" resultType="User">
select * from ${tableName}
</select>
</mapper>
执行SQL
指定命名空间与SqlId
@Test
public void testGetStarted() throws IOException {
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
final SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("com.example.start.springdemo.mybatis.dao.UserMapper.selectUser", 1L);
System.out.println("User:" + user);
User user2 = sqlSession.selectOne("com.example.start.springdemo.mybatis.dao.UserMapper.selectUserByType", 1L);
System.out.println(user2);
sqlSession.close();
}
简单概括
使用mybatis的步骤如下:
1)配置mybatis-config.xml 全局的配置文件 (1、数据源,2、外部的mapper) 2)创建SqlSessionFactory 3)通过SqlSessionFactory创建SqlSession对象 4)通过SqlSession操作数据库 CRUD 5)调用session.commit()提交事务 6)调用session.close()关闭会话
通过动态代理Mapper方式
定义Mapper接口
Mapper接口也就是DAO接口,接口全限定接口名(包名+接口名)即为SqlMap XML中的namespace,接口的方法名称为SqlMap中的SqlId.
如接口定义如下:
/**
* Alipay.com Inc.
* Copyright (c) 2004-2020 All Rights Reserved.
*/
package com.example.start.springdemo.mybatis.dao;
import java.util.List;
import com.example.start.springdemo.mybatis.User;
import org.apache.ibatis.annotations.Param;
/**
* @author xiele.xl
* @date 2020-06-04 11:04
*/
public interface UserMapper {
User selectUser(Long id);
User selectUserByType(Long id);
List<User> listUsers(@Param("tableName") String tableName);
}
Mapper 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.example.start.springdemo.mybatis.dao.UserMapper">
<resultMap id="UserMap" type="com.example.start.springdemo.mybatis.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="selectUser" resultMap="UserMap">
select * from spring_user where id = #{id}
</select>
<select id="selectUserByType" resultType="User">
select * from spring_user where id = #{id}
</select>
<select id="listUsers" resultType="User">
select * from ${tableName}
</select>
</mapper>
SqlSession.getMapper
@Test
public void testMapper() throws IOException {
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserByType(1L);
User user2 = mapper.selectUserByType(1L);
System.out.println("mapper query -> " + user + user2);
}
注: 通过Mapper接口调用,其底层也是获取完整的namespace+sqlId,即完整的statement
配置说明
属性(properties)
mybatis-config.xml中配置,这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
类型别名(typeAliases)
在mybatis-config.xml中配置,类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
Mapper文件
在mybatis-config.xml中配置。
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
这里所谓的mapper接口路径。实际上就是dao的接口路径。在mybatis中,通常把dao的包叫做mapper。类名,也叫做mapper
1、定义一个接口。
2、在接口所在的包中定义mapper.xml,并且要求xml文件和interface的名称要相同。
3、在mybatis-config.xml 中通过class路径,引入mapper(注解方式)。要求mapper.xml 中的名称空间是类的接口的全路径。
如下的方式为用注解来配置Mapper
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
Mapper XML文件详解
CRUD标签
select
select – 书写查询sql语句
select中的几个属性说明:
id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。
resultType:将结果集映射为java的对象类型。必须(和 resultMap 二选一)
parameterType:传入参数类型。可以省略
insert
insert 的几个属性说明:
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
useGeneratedKeys:开启主键回写,默认为false,
keyColumn:指定数据库的主键,一般指定为id
keyProperty:主键对应的pojo属性名,一般指定为id
标签内部:具体的sql语句。
update
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。
delete
delete 的几个属性说明:
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。
#{}和${}
{} 只是替换?,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。
${} 是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql,$可以是sql中的任一部分传入到Statement中,不能防止sql注入。
使用${} 去取出参数值信息,需要使用${value}
#{} 只是表示占位,与参数的名字无关,如果只有一个参数,会自动对应。
因此:推荐用[@Param](#)("xxx")
来解决SQL里的参数名称与接口方法里的参数名称一一对应,做到更好的可读性。
${} 占位符不会插入单引号,直接用原始字符串替换变量。
#{}占位符会在变量里插入一对单引号,''
因此使用#{}替换表名时,会出现异常:
如SQL为:
<select id="listUsers" resultType="User">
select * from #{tableName}
</select>
调用方式:
List<User> users = mapper.listUsers("spring_user");
System.out.println(users);
sqlSession.close();
会抛出如下异常:
Caused by: java.sql.SQLSyntaxErrorException:
You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version
for the right syntax to use near ''spring_user'' at line 1
在表名上添加单引号是非法的SQL。