Mybatis快速入门
概述
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。
相关介绍
持久层
javaEE的三层架构:表现层、业务层、持久层
负责将数据保存到数据库的那一层代码
Mybatis的简化
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql:///mybatis?useSSL=false";
String user="root";
String password="123456";
Connection conn= (Connection) DriverManager.getConnection(url,user,password);<configuration>
<!-- 类型别名-->
<!-- <typeAliases>-->
<!-- <typeAlias type="com.mybatis.pojo.User" alias="User"></typeAlias>-->
<!-- </typeAliases>-->
<!-- 包扫描的方式 自动扫描包中的po类,自动定义别名,别名是类名 -->
<typeAliases>
<package name="com.mybatis.pojo"/>
</typeAliases>
<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:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加载sql映射文件-->
<!-- <mapper resource="com/mybatis/mapper/UserMapper.xml"/>-->
<!-- 包扫描,可以不用写路径-->
<package name="com.mybatis.mapper"/>
</mappers>
</configuration>String sql="update tb_user set age=? where id=?";
PreparedStatement preparedStatement = conn.prepareStatement(sql);<select id="selectAll" resultType="user">
select *
from tb_user
</select>preparedStatement.setInt(1,26);
preparedStatement.setInt(2,2);
int count=preparedStatement.executeUpdate();例如封装查询User表的信息
Result rs=preparedStatement.executeQuery();
ArrayList<User>users=new ArrayList<>();
while(rs.next())
{
int id=rs.getInt("id");
String name=rs.getString("name");
............
...........
User user=new User();
user.setid(id);
user.setname(name);
.............
.............
users.add(user);
}// 获取Sqlsession对象,执行sql
SqlSession sqlSession =SessionFactory.openSession();
//执行sql
List<User> users = sqlSession.selectList("com.mybatis.mapper.UserMapper.selectAll");Mybatis几乎免除了所有的JDBC代码以及设置参数和获取结果集的工作
基本步骤
创建模块,导入mybatis坐标
编写Mybatis的核心配置文件->替换连接信息,解决硬编码问题
定义POJO类
编写SQL映射文件->统一管理SQL语句,也是解决硬编码问题
业务层使用
加载核心配置文件,获取SqlSessionFactory对象
获取Session对象,执行SQL
释放资源
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency><?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>
<!-- 别名-->
<typeAliases>
<!-- 包扫描-->
<package name="com.mybatis.pojo"/>
</typeAliases>
<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:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 加载sql映射文件-->
<!-- <mapper resource="com/mybatis/mapper/UserMapper.xml"/>-->
<!-- 包扫描,可以不用写路径-->
<package name="com.mybatis.mapper"/>
</mappers>
</configuration>package com.mybatis.pojo;
public class User {
private Integer id;
private String name;
private Integer _age;
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 Integer get_age() {
return _age;
}
public void set_age(Integer _age) {
this._age = _age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", _age=" + _age +
'}';
}
}

Mapper代理
问题:
在业务层使用的时候,使用sql的映射是使用namespace.id的方式,这里还是有硬编码的问题
而且使用的时候还需要去查看相关的sql映射id,并不是很方便
解决:
Mapper代理
步骤
- 定义与SQL映射文件同名的Mapper接口,两者要在同一目录下
- SQL映射文件的namespace要为Mapper接口的全限定名
- Mapper接口中定义方法,方法名为sql的id,保持参数类型和返回值相同
- 业务层编码
- SqlSession的getMapper方法获取Mapper接口的代理对象
- 代理对象调用方法完成sql
Mapper和SQL的映射文件在同一目录下,并且同名,则在mybatis的核心文件中加载SQL映射文件的时候可以使用包扫描的方式
<package name="com.mybatis.mapper"/>
resource下建多级目录xxx/xxx/xxx不能用xxx.xxx.xxx,创建与Mapper接口同路径,编译时会将两者放到一起



SQL映射文件
参数占位符:
#{}、${}
- #{}: 执行SQL时,会将#{}占位符替换为?,将来自动设置参数值 预编译
${}: 拼SQL。 会存在SQL注入问题
使用时机:
- 参数传递,都使用#{}
- 如果要对表名、列名进行动态设置,只能使用${}进行sql拼接。
parameterType:
对于有参数的mapper接口方法,我们在映射配置文件中应该配置 ParameterType 来指定参数类型。只不过该属性都可以 省略
用于设置参数类型,该参数可以省略,一般不写,在接口方法中有定义
SQL语句中特殊字符处理:
- 转义字符
<![CDATA[内容]1>(如<)
字段映射
有时候POJO类的字段名和数据库的字段名不一样,查询到的数据不能封装到实体类里面(会赋值为属性类型的默认值),所以在查询的时候需要做一个字段的映射
mapper接口中的映射方法参数对应的的是SQL语句的参数,这里数据类型的对应没有那么严格,SQL的结果封装对应的是resultType类型,如果要封装为POJO对象,则要求要字段一一对应
<select id="selectAll" resultType="user">
select id,name,age as _age
from tb_user
</select>sql片段
<sql id="_age">
id,name,age as _age
</sql>
<select id="selectAll" resultType="user">
select <include refid="_age"></include>
from tb_user
</select>- type:返回类型
- column:列名
- property:字段名
- select中的resultType要换成resultMap属性
<resultMap id="ageMap" type="user">
<result column="age" property="_age"/>
</resultMap>
<select id="selectByid" resultMap="ageMap">
select * from tb_user where id <![CDATA[
<
]]>#{id}
</select>条件查询
当查询语句中的参数多的时候,就需要方法参数映射的方式传入SQL语句中
<!-- 多条件查询-->
<select id="selectCD" resultMap="ageMap">
select *
from tb_user where name like #{name} and age like #{age};
</select>
多条件
@Param(“SQL参数占位符名称”) 参数名
List<User> selectCD(@Param("name") String name1,@Param("age") String age1);String name="李";
String age="1";
name="%"+name+"%";
age="%"+age+"%";
.................
.......
List<User> users = mapper.selectCD(name,age);对象封装
接口方法名(参数对象);
参数对象的属性名要与SQL的字段对应,如#{name},会去找 参数对象.name,所以也需要getter和setter方法,方法名没有严格要求,但一定要有返回对应字段。
SUser是age为String的类,这里是为了强调可以不用SQL返回结果的POJO类
List<User> selectCD(SUser user);SUser su=new SUser();
su.setge(age);
su.setName(name);
。。。。。。。。。。。
。。。。。。
List<User> users = mapper.selectCD(su);Map集合
会将Map集合里面的Key值对应的Value值映射到SQL对应字段
List<User> selectCD(Map map);HashMap<String, String> Map = new HashMap<String,String>();
Map.put("name","%李%");
Map.put("age","%1%");
.....................
....................
List<User> users = mapper.selectCD(Map);动态条件查询
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
有时候条件输入并不是都输入,这个时候需要缺少参数的多条件SQL语句也能执行
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
多条件-动态查询
<!-- 动态查询-多条件-->
<select id="Dycon_Mul" resultMap="ageMap">
select *
from tb_user
where
<if test="name !='' and name!=null">
name=#{name}
</if>
<if test="age!='' and age!=null">
and age=#{age}
</if>
</select>// 多条件动态
List<User> Dycon_Mul(SUser su);import com.mybatis.mapper.UserMapper;
import com.mybatis.pojo.SUser;
import com.mybatis.pojo.User;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class Dycon_Multest {
public static void main(String[] args) throws IOException {
String s = "mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(s);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SUser su = new SUser();
su.setName("zs");
//不输入age参数
// su.setge("20");
List<User> users = mapper.Dycon_Mul(su);
for(User u :users){
System.out.println(u);
}
}
}可以看到name没有在sql语句中
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Preparing: select * from tb_user where name=?
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Parameters: zs(String)
[DEBUG] [main] c.m.m.U.Dycon_Mul - <== Total: 2
User{id=6, name='zs', _age=20}
User{id=7, name='zs', _age=21}问题
但是可以发现这个sql写法有一个缺陷,就是当第一个参数省略时,拼接的sql是不合法的
import com.mybatis.mapper.UserMapper;
import com.mybatis.pojo.SUser;
import com.mybatis.pojo.User;
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.IOException;
import java.io.InputStream;
import java.util.List;
public class Dycon_Multest {
public static void main(String[] args) throws IOException {
String s = "mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(s);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SUser su = new SUser();
//省略名字
// su.setName("zs");
su.setge("20");
List<User> users = mapper.Dycon_Mul(su);
for(User u :users){
System.out.println(u);
}
}
}
可以看到sql语句为select * from tb_user where and age=?,这是不合法的
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Preparing: select * from tb_user where and age=?
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Parameters: 20(String)
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 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 'and age='20'' at line 6
解决方案
应该要想办法将拼接的sql语句变成合法
1、恒等式让所有条件格式都一样
<!-- 动态查询-多条件-->
<select id="Dycon_Mul" resultMap="ageMap">
select *
from tb_user
where 1=1
<if test="name !='' and name!=null">
and name=#{name}
</if>
<if test="age!='' and age!=null">
and age=#{age}
</if>
</select>select * from tb_user where 1=1 and age=?
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Preparing: select * from tb_user where 1=1 and age=?
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Parameters: 20(String)
[DEBUG] [main] c.m.m.U.Dycon_Mul - <== Total: 1
User{id=6, name='zs', _age=20}2、<where>标签替换where
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<!-- 动态查询-多条件-->
<select id="Dycon_Mul" resultMap="ageMap">
select *
from tb_user
<where>
<if test="name !='' and name!=null">
and name=#{name}
</if>
<if test="age!='' and age!=null">
and age=#{age}
</if>
</where>
</select>可以看到where自动省略了name
select * from tb_user WHERE age=?
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Preparing: select * from tb_user WHERE age=?
[DEBUG] [main] c.m.m.U.Dycon_Mul - ==> Parameters: 20(String)
[DEBUG] [main] c.m.m.U.Dycon_Mul - <== Total: 1
User{id=6, name='zs', _age=20}单条件-动态查询
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<!-- 单条件查询-->
<select id="Dycon_Sin" resultType="User">
select * from tb_user
where
<choose>
<when test="name !='' and name!= null">
name = #{name}
</when>
<when test="age!=''and age!=null">
age=#{age}
</when>
</choose>
</select>public class Dycon_Sintest {
@Test
public static void main(String[] args) throws IOException {
String s = "mybatis-config.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(s);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SUser su=new SUser();
//这里设置了两个参数
su.setge("20");
su.setName("zs");
List<User> users = mapper.Dycon_Sin(su);
for(User u :users){
System.out.println(u);
}
}
}select * from tb_user where name = ?
可以看到只用了SQL语句中的第一个参数
[DEBUG] [main] c.m.m.U.Dycon_Sin - ==> Preparing: select * from tb_user where name = ?
[DEBUG] [main] c.m.m.U.Dycon_Sin - ==> Parameters: zs(String)
[DEBUG] [main] c.m.m.U.Dycon_Sin - <== Total: 2
User{id=6, name='zs', _age=null}
User{id=7, name='zs', _age=null}问题
当没有输入值时,就会出现SQL语句不合法的情况,比如select * from tb_user where,所以需要一个default的情况
解决方案
1、<otherwise>标签
例如:
<!-- 单条件查询-->
<select id="Dycon_Sin" resultMap="ageMap">
select * from tb_user
where
<choose>
<when test="name !='' and name!= null">
name = #{name}
</when>
<when test="age!=''and age!=null">
age=#{age}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</select>
那么产生的SQL语句为select * from tb_user where 1=1 ,就可以避免报错
2、<where>标签
像上面的otherwise情况,只是为了SQL语句合法而写的
使用where标签可以省略otherwise部分
<!-- 单条件查询-->
<select id="Dycon_Sin" resultMap="ageMap">
select * from tb_user
<where>
<choose>
<when test="name !='' and name!= null">
name = #{name}
</when>
<when test="age!=''and age!=null">
age=#{age}
</when>
</choose>
</where>
</select>
结果:
会自动把SQL语句中的where去除,保证语句的合法
select * from tb_use
[DEBUG] [main] c.m.m.U.Dycon_Sin - ==> Preparing: select * from tb_user
[DEBUG] [main] c.m.m.U.Dycon_Sin - ==> Parameters:
[DEBUG] [main] c.m.m.U.Dycon_Sin - <== Total: 7
User{id=1, name='zhangsan', _age=22}
User{id=2, name='lisi', _age=26}
User{id=3, name='wangwu', _age=17}
User{id=4, name='李四', _age=19}
User{id=5, name='李三', _age=18}
User{id=6, name='zs', _age=20}
User{id=7, name='zs', _age=21}
Process finished with exit code 0
