Mybatis
1.Mybatis简介
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
1.1.Mybatis框架的意义
-
简化了JDBC开发.(JDBC :获取连接 数据操作对象 执行sql语句 处理结果集 释放资源,以上步骤是重复的事情),提高了开发效率。
-
Mybatis统一数据库操作规范。有利于代码的维护。
-
Mybatis中强大的映射功能,支持各种复杂的查询。
-
Mybatis中动态SQL的功能,简化了条件查询
-
简单易学,本身就很小且简单。没有任何第三方依赖。
-
灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。
-
解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
-
提供映射标签,支持对象与数据库的orm字段关系映射,mybatis也属于ORM框架。
1.2.ORM关系映射
ORM框架: Hibernate Mybatis Spring DATA JPA 等等
ORM :
O :Object
R : Relational
M : Mapping
对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据的关系.
将数据库中的数据与程序中对应进行绑定。最终可实现,操作对象及可操作数据库。
2.Mybatis使用
2.1.Mybatis入门使用
2.1.1.下载mybatis相关jar包
mybatis-x.x.x.jar
2.1.2.在项目中引入mybatis核心jar包
2.1.3.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>
<!-- 运行环境 -->
<!-- default : 采用环境 -->
<environments default="dev">
<!-- 具体的环境 id 具体环境唯一标识-->
<environment id="dev">
<!-- 事务管理器 -->
<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/1009_cms"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<!-- 测试环境要接近生产环境 尽可能和生产环境保持一致 -->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 具体的映射器 -->
<!-- 注解 xml(重点) -->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
2.1.4.映射配置文件
<?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">
<!--
namespace : 命名空间
-->
<mapper namespace="com.zw.pojo.User">
<!--
查询指令标签
id : 查询指令的唯一标识
resultType : 查询结果映射的类
-->
<select id="selectOne" resultType="com.zw.pojo.User">
select id as id,user_name as userName,password as password,real_name as realName from c_user where id = 1
</select>
<select id="selectAll" resultType="com.zw.pojo.User">
select id as id,user_name as userName,password as password,real_name as realName from c_user
</select>
</mapper>
2.1.5.dao
package com.zw.dao;
import java.io.IOException;
import java.io.InputStream;
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.zw.pojo.User;
public class UserDao {
public User selectOne() {
User user = null;
try {
//InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 1. 找到配置文件
InputStream inputStream = UserDao.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
// 2. 根据配置文件生成数据库会话对象
//获取SqlSessionFactory构建器
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 解析配置文件 构建一个SqlSession 工厂
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 根据SqlSession 工厂 获取SqlSession会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 3. 使用会话对象操作数据库
user = sqlSession.selectOne("com.zw.pojo.User.selectOne");
System.out.println(user);
//4.释放资源
sqlSession.close();
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
/**
* @Title: selectAll
* @author: KevinZeng
* @date: 2019年12月28日 上午10:46:05
* @Description: 查询所有
* @return
* @return: List<User>
*/
public List<User> selectAll(){
//1. 找到资源文件
List<User> users = null;
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建SqlSessionFactory构建器
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//3. 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
//4. 获取数据库会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//5. 进行数据操作
users = sqlSession.selectList("com.zw.pojo.User.selectAll");
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
} catch (Exception e) {
e.printStackTrace();
}
return users;
}
public static void main(String[] args) {
new UserDao().selectAll();
}
}
2.2.mybatis普通CRUD
<?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">
<!--
namespace : 命名空间
-->
<mapper namespace="com.zw.pojo.User">
<insert id="insert" parameterType="com.zw.pojo.User">
insert into user (name,password) value (#{name},#{password})
</insert>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="selectOne" resultType="com.zw.pojo.User">
select id,name,password from user where id = #{id}
</select>
<select id="selectAll" resultType="com.zw.pojo.User">
select id,name,password from user
</select>
<update id="update" parameterType="com.zw.pojo.User">
update user set name = #{name} ,password = #{password}
where id = #{id}
</update>
</mapper>
package com.zw.dao.impl;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.zw.dao.IUserDao;
import com.zw.pojo.User;
import com.zw.util.SqlSessionUtil;
public class UserDaoImpl implements IUserDao {
@Override
public int insert(User user) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
int m = sqlSession.insert("com.zw.pojo.User.insert",user);
//在使用 mybatis 进行数据更新操作时 mybatis默认是不会提交事务
//需要手动提交
sqlSession.commit();
sqlSession.close();
return m;
}
@Override
public int delete(Integer id) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
int m = sqlSession.delete("com.zw.pojo.User.delete",id);
sqlSession.commit();
sqlSession.close();
return m;
}
@Override
public User selectOne(Integer id) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
User user = sqlSession.selectOne("com.zw.pojo.User.selectOne",id);
sqlSession.close();
return user;
}
@Override
public List<User> selectAll() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
List<User> users = sqlSession.selectList("com.zw.pojo.User.selectAll");
sqlSession.close();
return users;
}
@Override
public int update(User user) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
int m = sqlSession.update("com.zw.pojo.User.update", user);
sqlSession.commit();
sqlSession.close();
return m;
}
public static void main(String[] args) {
IUserDao userDao = new UserDaoImpl();
List<User> users = userDao.selectAll();
System.out.println(users);
System.out.println(userDao.selectOne(2));
User user = new User("456", "456");
//添加
//userDao.insert(user);
//删除
//userDao.delete(4);
//修改
user = new User("110", "110");
user.setId(2);
userDao.update(user);
}
}
2.3.Mybatis接口式进行CRUD
2.3.1.映射文件中的namespace的值:CRUD接口的全路径
<mapper namespace="com.zw.dao.IUserDao">
写错时,会出现以下异常:
2.3.2.映射文件中命令标签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">
<!--
namespace : 命名空间
-->
<mapper namespace="com.zw.dao.IUserDao">
<insert id="insert" parameterType="com.zw.pojo.User">
insert into user (name,password) value (#{name},#{password})
</insert>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<select id="selectOne" resultType="com.zw.pojo.User">
select id,name,password from user where id = #{id}
</select>
<select id="selectAll" resultType="com.zw.pojo.User">
select id,name,password from user
</select>
<update id="update" parameterType="com.zw.pojo.User">
update user set name = #{name} ,password = #{password}
where id = #{id}
</update>
</mapper>
package com.zw.dao;
import java.util.List;
import com.zw.pojo.User;
public interface IUserDao {
/**
* @Title: insert
* @author: KevinZeng
* @date: 2019年12月28日 上午11:43:59
* @Description: 新增用户
* @param user
* @return
* @return: int
*/
public int insert(User user);
/**
* @Title: delete
* @author: KevinZeng
* @date: 2019年12月28日 上午11:44:17
* @Description: 根据ID 删除用户
* @param id
* @return
* @return: int
*/
public int delete(Integer id);
/**
* @Title: selectOne
* @author: KevinZeng
* @date: 2019年12月28日 上午11:44:40
* @Description: 根据ID 查询用户
* @param id
* @return
* @return: User
*/
public User selectOne(Integer id);
/**
* @Title: selectAll
* @author: KevinZeng
* @date: 2019年12月28日 上午11:45:14
* @Description: 查询所有用户
* @param user
* @return
* @return: List<User>
*/
public List<User> selectAll();
/**
* @Title: update
* @author: KevinZeng
* @date: 2019年12月28日 上午11:45:29
* @Description: 修改用户信息
* @param user
* @return
* @return: int
*/
public int update(User user);
}
package com.zw.test;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.zw.dao.IUserDao;
import com.zw.pojo.User;
import com.zw.util.SqlSessionUtil;
public class Test {
public static void main(String[] args) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
//获取接口的代理类
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//查询所有数据
List<User> users = userDao.selectAll();
System.out.println(users);
User user = userDao.selectOne(1);
System.out.println(user);
//userDao.insert(new User("789", "789"));
// userDao.delete(6);
//User user1 = new User("Hello", "Mybatis");
//user1.setId(2);
//userDao.update(user1);
sqlSession.commit();
sqlSession.close();
}
}
3.Mybatis配置文件
3.1.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">
<!-- 根标签 表示mybatis 运行环境配置-->
<configuration>
//一、properties标签
<!--
mybatis可以使用properties来引入外部properties配置文件的内容
resource:引入类路径下的资源
url:引入网络路径或磁盘路径下的资源
-->
<properties resource="jdbc.properties"></properties>
//二、settings标签
<!--
settings包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="LOG4J"/>
</settings>
//三、typeAliases标签:别名处理器,可以为我们的java类型起别名
别名不区分大小写
<typeAliases>
<!--
typeAliases:为某个java类型起别名
type:指定要起别名的类型全类名;默认别名就是类名小写
alias:指定新的别名
-->
<!--为指定的类 指定别名-->
<!--<typeAliases type="com.sxt.pojo.Employee" alias="emp"/>-->
<!--
package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认的别名,别名就是类名
-->
<package name="com.zw.bean"/>
<!--批量起别名的情况下,使用@Alias注解为某个类型指定新的别名-->
</typeAliases>
//四、typeHandlers标签:类型处理器,解决java属性和数据库字段类型兼容问题
<typeHandlers>
//了解即可
</typeHandlers>
//五、plugins标签:mybatis插件配置
拦截以下四大对象
- Executor执行处理器(update,query,flushStatements,commit,rollback.getTransaction,close,isClosed)
- ParameterHandler参数处理器(getParameterObject,setParameters)
- ResultSetHandler结果集处理器(handlerResultSets,handlerOutputParameters)
- StatementHandler 处理sql的处理器(prepare,parameterize,batch,update,query)
<!-- 分页插件 -->
<!--
<plugins>
<plugin interceptor="拦截器的全路径"></plugin>
</plugins>
-->
//六、environments标签:环境们,mybatis可以配置多种环境,default指定使用某种环境,可以快速切换
environment:配置一个具体的环境信息:必须有2个标签;id代表当前环境的唯一标识
transactionManager:事务管理器
type:事务管理器的类型,mybatis提供2种:
| JDBC(JdbcTransactionFactory) 使用JDBC的事务管理器 提交或者回滚事务
| MANAGED(ManagedTransactionFactory) 从不提交和回滚事务
自定义事务管理器:实现TransactionFactory接口,type指定为全类名
dataSource:数据源;
type:数据源类型,mybatis内置3种:
| UNPOOLED(UnpooledDataSourceFactory)每次都创建新的连接。不是用连接池
| POOLED(PooledDataSourceFactory)使用连接池 推荐
| JNDI(JndiDataSourceFactory)应服务器连接 不使用
自定义数据源:实现DataSourceFactory接口,type是全类名
<environments default="development">
<!-- 具体的环境 id 具体环境唯一标识-->
<environment id="dev">
<!--事务管理器 -->
<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="root"/>
</dataSource>
</environment>
</environments>
//七、databaseIdProvider支持多数据库厂商的;
type="DB_VENDOR" :VendorDatabaseIdProvider
作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql
MySQL,Oracle,SQL Server,xxxx
然后在mybatis映射文件sql语句的标签里面指定databaseId的别名即可
<databaseIdProvider type="DB_VENDOR">
<!--为不同的数据库厂商起别名-->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
//八、mappers:将sql映射注册到全局配置中
<mappers>
<!--
mapper:注册一个sql映射
resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mapper/AuthorMapper.xml
class :使用类的完全限定名 要求: 配置文件名称和接口名称一致且同包
<package name="org.mybatis.builder"/> : 将包内的映射器接口实现全部注册为映射器
-->
<!-- 具体的映射器 -->
<mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
</mappers>
</configuration>
3.2.Mybatis映射文件
<?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">
<!--
namespace : 命名空间
namespace作用:
1.namespace + id 生成key,namespace 区分sql命令 sql命令的ID 不能重复
2.使用代理时 根据namespace 生成代理类
-->
<mapper namespace="com.zw.dao.IUserDao">
<!--
cache : 表示开启二级缓存
-->
<!-- 结果映射关系
配置映射关系
列和属性的关系 解决列名和属性名不一致
-->
<resultMap type="User" id="BASE_RESULT_MAP">
<result column="id" property="id"/>
<result column="userName" property="name"/>
<result column="password" property="password"/>
</resultMap>
<!--
sql片段
将公共的sql 抽离出来
可以在多处引用
-->
<sql id="BASE_CLOUNM">
id,name,password
</sql>
<!--
id : 当前namespace中唯一标识
parameterType : 参数类型 可以缺省 : map
parameterMap : 已经废弃
useGeneratedKeys : 是否使用JDBC内部生成主键
keyColumn : 生成自增涨主键的列名
keyProperty : 列对应类中属性名
-->
<insert id="insert" keyColumn="id" keyProperty="id" useGeneratedKeys="true" parameterType="com.zw.pojo.User">
insert into user (name,password) value (#{name},#{password})
</insert>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
<!--
id : 当前 namespace中唯一标识
parameterType : 参数类型 可缺省
resultType : 查询结果对应的类
resultMap : 查询结果 对应的映射关系
注意:resultType 和 resultMap 只能使用1个,并且select标签必须有其中一个
-->
<select id="selectOne" resultMap="BASE_RESULT_MAP">
select
<include refid="BASE_CLOUNM" />
from user where id = #{id}
</select>
<select id="selectAll" resultType="com.zw.pojo.User">
select id,name,password from user
</select>
<update id="update" parameterType="com.zw.pojo.User">
update user set name = #{name} ,password = #{password}
where id = #{id}
</update>
</mapper>
3.3.模糊查询
方式一:concat函数(推荐)
<select id="selectOne" parameterType="String" resultType="User">
select id,name,password from user where name like concat('%',#{name},'%');
</select>
方式二:
<select id="selectOne2" parameterType="String" resultType="User">
select id,name,password from user where name like '%${name}%';
</select>
方式三:bind标签
<select id="selectOne5" parameterType="String" resultType="User">
<!--
name 别名
value : 要查询字符串 预处理
-->
<bind name="keywords" value="'%'+ new String(name) + '%'"/>
select id,name,password from user where name like #{keywords};
</select>
3.4.#号与$比较
在mybatis中,使用#{}会对sql语句进行预处理,而$则是字符串拼接。#{}的sql,在一定程度上会防止sql注入攻击。而$,则只是字符串拼接,无法防止sql注入攻击。
一般情况下,能使用#{}就不要使用${}.
由于,#{}使用PreparedStatement设置参数,会对字符串进行转义,前后拼接单引号。此时这个字符串就是一个字符串。无法表示列名。所以#{}这种方式,是不支持数据库关键字相关操作设置。
而${}是字符串拼接,不会对字符串进行特殊处理。所以可以支持数据库关键字相关操作设置。
3.5.分页查询
方式一:使用阿里分页插件:PageHelper
-
引入相关jar包:
pagehelper-5.1.10.jar jsqlparser-2.1.jar
-
在核心配置文件中,配置分页插件
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
-
开启分页
//开启分页 永远只对最近一次查询生效 //若下次查询也需要进行分页查询 需要重新开启 Page<User> pageInfo = PageHelper.startPage(1, 10); userDao.selectPage("0"); System.out.println("当前页码:"+pageInfo.getPageNum()); System.out.println("最大页数"+pageInfo.getPages()); System.out.println("符合条件数据行数:"+pageInfo.getTotal()); System.out.println("每页数据行数:"+pageInfo.getPageSize()); System.out.println("查询具体数据:"+pageInfo.getResult()); for (User user : pageInfo.getResult()) { System.out.println(user); }
<select id="selectPage" parameterType="String" resultType="User"> select id,name,password from user where name like concat('%',#{name},'%') </select>
方式二:RowBounds进行分页
<select id="selectPage3" parameterType="String" resultType="User"> select id,name,password from user where name like concat('%',#{name},'%') </select>
public List<User> selectPage3(RowBounds rowBounds,String name); RowBounds rowBounds = new RowBounds(0,10); userDao.selectPage3(rowBounds,"0");
3.6.ResultMap
结果映射,由于命名的原因,表的列名往往与Java类中属性名不一致,而Mybatis 默认是根据查询结果的列名查找对应的类的属性名,从而将该列的值,设置到类对象的该属性中。但是,若不一致,则该属性没有值。
解决这个问题,主要两种方案:
- 使用as 关键字,为列取别名,保持别名与类中属性名一致。
- 使用ResultMap指定列和属性的映射关系
<!--
type : 具体的类的全路径
id : resultMap的唯一标识
|-- <id/> 表示主键 查询结果的主键的列 只是标识这个列是主键列
|-- column : 列名
|-- property : 对应type的属性名
|-- <result/> 表查询结果的一个列
|-- column : 列名
|-- property : 对应type的属性名
-->
<resultMap type="com.zw.pojo.User" id="BASE_RESULT_MAP">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="password" property="password"/>
</resultMap>
<select id="selectOne" parameterType="String" resultMap="BASE_RESULT_MAP">
select id,name,password from user where name like concat('%',#{name},'%')
</select>
3.7.sql片段
在mybatis中,为了提高sql的复用性。mybatis提供了sql标签,可以将需要复用的sql语句包裹起来,然后在需要使用的地方直接引入这个sql片段即可。
<sql id="BASE_ALL_COLUMN">
id,name,password
</sql>
<select id="selectList" parameterType="String" resultMap="BASE_RESULT_MAP">
select
<include refid="BASE_ALL_COLUMN"></include>
from user where name like concat('%',#{name},'%')
</select>
注意: 使用include引入这个sql片段
3.8.动态sql
简化sql根据不同的条件生成不同的sql的问题。
例如 :根据ID 更新用户 : User 对象 里面值 生成响应sql语句 动态sql
name : update user set name = “新name” where id = id
name 和 sex : update user set name = “新name” , sex = “新sex” where id = id
update user set name = “新name” ,sex = “新sex”,age = “新age” where id = id值 :// user 里面 只有ID 值 name值
//本来 只需要一步操作 : update
//1. 根据ID 查询用户
//2. 设置用户需要修改的属性值
//3.更新
mybatis为了实现以上功能,提供了以下标签:
3.8.1.if标签
<if test = "表达式" >
sql语句
</if>
若如果表达式最终运算结果为true,则将sql语句进行拼接,从而进行执行
示例:
<select id="selectList" resultMap="BASE_RESULT_MAP" parameterType="Student">
select
<include refid="BASE_ALL_COLUMN" />
from
student
where 1=1
<if test="id != null and id !=''">
and id = #{id}
</if>
<if test="name != null and name !=''">
and name like concat('%',#{name},'%')
</if>
<if test="minAge != null and minAge !=''">
and age > #{minAge}
</if>
<if test="maxAge != null and maxAge !=''">
and #{maxAge} > age
</if>
</select>
**注意:**如果不支持字符特殊符号,可以使用:<![CDATA[ 字符串… ]]>转义
3.8.2.choose(when … otherwise)
多条件分支判断,自上而下判断,判断上面条件后执行相应的sql,后面的条件表达式不会运行。若所有when中的表达式都不成立,则执行otherwise的程序
<select id="selectList2" resultMap="BASE_RESULT_MAP">
select
<include refid="BASE_ALL_COLUMN" />
from
student
where 1=1
<choose>
<when test="age != null and age > 10">
age > 10
</when>
<when test="age != null and age > 8">
age > 8
</when>
<otherwise>
age > 0
</otherwise>
</choose>
</select>
/**
* @Title: selectList2
* @author: KevinZeng
* @date: 2019年12月30日 下午11:55:14
* @Description: 根据条件查询
* @param student
* @return
* @return: List<Student>
*/
public List<Student> selectList2(Map<String,Object> student);
Map<String, Object> param = new HashMap<>();
param.put("age", 11);
List<Student> students = studentDao.selectList2(param);
for (Student student : students) {
System.out.println(student);
}
sqlSession.commit();
sqlSession.close();
3.8.3.where
由于在mybatis动态sql中,第一个条件可能成立也可能不成立,所以后面条件都需要and关键字。此时拼接sql语句,会出现可能多一个and。mybatis 为了解决该问题,提供了where标签,where用于取代where关键字。
被where标签包裹的条件成立,则自动在sql后面追加where关键字 且会去除第一个and 关键字。
<select id="selectList3" resultMap="BASE_RESULT_MAP">
select
<include refid="BASE_ALL_COLUMN" />
from
student
<where>
<choose>
<when test="age != null and age > 10">
and age > 10
</when>
<when test="age != null and age > 8">
and age > 8
</when>
</choose>
</where>
</select>
3.8.4.set
set标签取代sql语句中的set关键字,自动添加set关键字 ,且去除最后一个逗号。
<update id="update">
update student
<set>
<if test="name != null and name !=''">
name = #{name},
</if>
<if test="age != null and age !=''">
age = #{age},
</if>
<if test="sex != null and sex !=''">
sex = #{sex},
</if>
<if test="phone != null and phone !=''">
phone = #{phone},
</if>
</set>
where id = #{id}
</update>
3.8.5.foreach
循环标签。
<select id="selectList4" resultMap="BASE_RESULT_MAP">
select <include refid="BASE_ALL_COLUMN" />
from student
<!--
collection : 待循环集合
item : 每次循环的集合元素别名
open : 循环前 字符串
close : 循环后的字符串
separator : 每次中间字符串
index :下标索引
-->
<foreach collection="ids" item="id" open=" where 1=1 and id in(" close=" ) and 1=1" separator=",">
#{id}
</foreach>
</select>
3.8.6.trim
prefix : 新增前缀
prefixOverrides : 去除的前缀
suffix : 新增的后缀
suffixOverrides : 去除的后缀
<update id="update2">
update student
<!--
prefix : 新增前缀
prefixOverrides : 去除的前缀
suffix : 新增的后缀
suffixOverrides : 去除的后缀
-->
<trim prefix="set" prefixOverrides="and" suffixOverrides="and|or">
<if test="name != null and name !=''">
and name = #{name} or
</if>
<if test="age != null and age !=''">
age = #{age},
</if>
<if test="sex != null and sex !=''">
sex = #{sex},
</if>
<if test="phone != null and phone !=''">
phone = #{phone},
</if>
</trim>
where id = #{id}
</update>
3.9.联表查询
多张表联合查询。多张表联合查询的结果数据的处理方式。由于一般,一张表对应一个类,但是联表查询时,往往是多张表的数据(多张表的列名),而其没有具体的对应的类。所以其查询的结果需要特殊处理。一般可以使用以下方式处理:
3.9.1.在类中扩展属性
C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.java和Student.java
public class Classes {
private Integer id;
private String name;
}
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
private String phone;
private Integer classId;
private String className;
}
//省略getters and setters 和 toString 构造
S t u d e n t M a p p e r . j a v a StudentMapper.java StudentMapper.java
public interface StudentMapper {
public List<Student> selectAll();
}
S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml
<mapper namespace="com.zw.mapper.StudentMapper">
<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<result column="phone" property="phone"/>
<result column="class_id" property="classId"/>
<result column= "className" property="className"/>
</resultMap>
<sql id="BASE_ALL_COLUMN">
id,name,age,sex,phone,
</sql>
<!--查询所有的学生信息包括所有的班级信息-->
<select id="selectAll" resultMap="BASE_RESULT_MAP">
SELECT
st.id,
st.name,
st.age,
st.sex,
st.phone,
cs.name as className
FROM
student st
LEFT JOIN classes cs ON st.class_id = cs.id;
</select>
</mapper>
3.9.2.使用Map接收查询结果
C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.java和Student.java
public class Classes {
private Integer id;
private String name;
}
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
private String phone;
}
//省略getters and setters 和 toString 构造
S t u d e n t M a p p e r . j a v a StudentMapper.java StudentMapper.java
public interface StudentMapper {
public List<Map<String,Object>> selectAll();// 使用Map接收查询结果
}
S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml
<mapper namespace="com.zw.mapper.StudentMapper">
<!-- 查询所有的学生信息包括其班级信息 使用Map接收查询结果 -->
<select id="selectAll" resultType="map">
SELECT
st.id,
st.name,
st.age,
st.sex,
st.phone,
cl.name as className
FROM
student st
LEFT JOIN classes cl ON st.class_id = cl.id;
</select>
</mapper>
测 试 类 测试类 测试类
public static void main(String[] args) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Map<String, Object>> students = mapper.selectAll();
System.out.println(students);
sqlSession.close();
}
3.9.3.二次查询
先查学生,然后根据班级ID查询班级
C
l
a
s
s
e
s
.
j
a
v
a
和
S
t
u
d
e
n
t
.
j
a
v
a
Classes.java 和 Student.java
Classes.java和Student.java
public class Student {
private Integer id;
private String name;
private Integer age;
private String sex;
private String phone;
private Integer classId;
private String className;
}
public class Classes {
private Integer id;
private String name;
}
//省略getters and setters 和 toString 构造
S t u d e n t M a p p e r . j a v a 和 C l a s s e s M a p p e r . j a v a StudentMapper.java 和 ClassesMapper.java StudentMapper.java和ClassesMapper.java
public interface ClassesMapper {
public Classes selectById(@Param("id")Integer id);//根据班级ID查询班级信息
}
public interface StudentMapper {
public List<Student> selectAll();
}
C l a s s e s M a p p e r . x m l ClassesMapper.xml ClassesMapper.xml
<mapper namespace="com.zw.mapper.ClassesMapper">
<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
<id column="id" property="id"/>
<result column="name" property="name"/>
</resultMap>
<select id="selectById" resultMap="BASE_RESULT_MAP">
SELECT
id,name
FROM
classes
WHERE
id = #{id}
</select>
</mapper>
S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml
<mapper namespace="com.zw.mapper.StudentMapper">
<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
<result column="sex" property="sex" />
<result column="phone" property="phone" />
<result column="class_id" property="classId"/>
</resultMap>
<sql id="BASE_ALL_COLUMN">
<!-- 数据库中student表的各个字段 -->
id, name, age, sex, phone,class_id
</sql>
<!--先查询所有的学生 根据学生中班级ID 查询班级信息 -->
<select id="selectAll" resultMap="BASE_RESULT_MAP">
SELECT
<include refid="BASE_ALL_COLUMN"></include>
FROM
student
</select>
</mapper>
测 试 类 测试类 测试类
package com.zw.test;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.zw.mapper.ClassesMapper;
import com.zw.mapper.StudentMapper;
import com.zw.pojo.Classes;
import com.zw.pojo.Student;
import com.zw.util.SqlSessionUtil;
/**
* @ClassName: Test
* @Description:封装查询结果
* @author: KevinZeng
* @date: 2020年1月2日 上午1:00:24
*/
public class Test {
public static void main(String[] args) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
ClassesMapper classMapper = sqlSession.getMapper(ClassesMapper.class);
//使用map接收查询结果
List<Student> sts = studentMapper.selectAll();
for (int i = 0; i < sts.size(); i++) {
Student st = sts.get(i); // 6个
Integer classId = st.getClassId(); // 班级ID
Classes classes = classMapper.selectById(classId);
//根据班级ID 进行查询班级 一般而言 6 次
//然后班级只有3个 所以最多只查询3次 因为已经查询过的数据 放入了缓存中 会先从缓存中检查是否存在对应的数据
//若存在 则直接从缓存中取 不会进行查询。
sts.get(i).setClassName(classes.getName());
System.out.println(st);
}
}
}
3.9.4.mybatis解决方案
在mybatis中,表和表之间存在联系,数据与数据存在关系。多对一,一对多,多对多。这些数据的关系,只是角度不同。基于这一点,mybatis,提供了标签:association
方案一:封装查询结果
C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.java和Student.java
public class Classes {//班级实体类
private Integer id;
private String name;
}
public class Student {//学生实体类
private Integer id;
private String name;
private Integer age;
private String sex;
private String phone;
private Classes classes;//学生对应的班级
}
S t u d e n t M a p p e r . j a v a StudentMapper.java StudentMapper.java
public interface StudentMapper {
public List<Student> selectAll();
}
S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml
<mapper namespace="com.zw.mapper.StudentMapper">
<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
<result column="sex" property="sex" />
<result column="phone" property="phone" />
<!-- 多对一
association : 表示多对一
property : 所属类中某个对应的属性
javaType :属性的类型
-->
<association property="classes" javaType="com.zw.pojo.Classes">
<result column="class_id" property="id" />
<result column="className" property="name" />
</association>
</resultMap>
<select id="selectAll" resultMap="BASE_RESULT_MAP">
SELECT
st.id,st.name,st.age,st.sex,st.phone,st.class_id ,cs.name as className
FROM
student st
LEFT JOIN
classes cs
ON
st.class_id = cs.id
</select>
</mapper>
T e s t . c l a s s 测 试 类 Test.class测试类 Test.class测试类
public class Test {
public static void main(String[] args) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = studentMapper.selectAll();
for (int i = 0; i < students.size(); i++) {
// 获取此学生拥有的班级ID
System.out.println(students.get(i));
}
sqlSession.close();
}
}
方式二:使用二次查询
C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.java和Student.java
public class Classes {//班级实体类
private Integer id;
private String name;
}
public class Student {//学生实体类
private Integer id;
private String name;
private Integer age;
private String sex;
private String phone;
private Classes classes;//学生对应的班级
}
//省略toString()方法和getters and setters
S t u d e n t M a p p e r . j a v a 和 C l a s s e s M a p p e r . j a v a StudentMapper.java 和 ClassesMapper.java StudentMapper.java和ClassesMapper.java
public interface StudentMapper {
public List<Student> selectAll();
}
public interface ClassesMapper {
public Classes selectById(@Param("id")Integer id);//根据班级ID查询班级信息
}
S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml
<mapper namespace="com.zw.mapper.StudentMapper">
<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
<result column="sex" property="sex" />
<result column="phone" property="phone" />
<!-- 多对一
association : 表示多对一
property : 所属类中某个对应的属性
javaType :属性的类型
-->
<association property="classes" javaType="com.zw.pojo.Classes" select="com.zw.mapper.ClassesMapper.selectById" column="class_id"></association>
</resultMap>
<sql id="BASE_ALL_COLUMN">
<!-- 数据库中student表的各个字段 -->
id, name, age, sex, phone,class_id
</sql>
<!--先查询所有的学生 根据学生中班级ID 查询班级信息 -->
<select id="selectAll" resultMap="BASE_RESULT_MAP">
SELECT
<include refid="BASE_ALL_COLUMN"></include>
FROM
student
</select>
</mapper>
C l a s s e s M a p p e r . x m l ClassesMapper.xml ClassesMapper.xml
<mapper namespace="com.zw.mapper.ClassesMapper">
<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
<id column="id" property="id"/>
<result column="name" property="name"/>
</resultMap>
<!-- 根据班级ID 查询班级 -->
<select id="selectById" resultMap="BASE_RESULT_MAP">
SELECT
id,name
FROM
classes
WHERE
id = #{id}
</select>
</mapper>
3.9.5.一对多
mybatis的实现方式:
方式一:封装查询结果
<!--
一对多
collection : 表示一对多
property : 表示类中属性
javaType : 属性的类型
ofType :属性中本身的数据类型
-->
<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="sts" javaType="list" ofType="Student" >
<id column="stId" property="id"/>
<result column="stName" property="name"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<result column="phone" property="phone"/>
<result column="class_id" property="classId"/>
</collection>
</resultMap>
<select id="selectAll" resultMap="BASE_RESULT_MAP">
select cs.id,cs.name,st.id as stId,st.name as
stName,st.age,st.sex,st.phone,st.class_id from classes cs LEFT JOIN
student st on cs.id = st.class_id
</select>
public class Classes {
public Integer id;//班级ID
public String name;//班级名称
public List<Student> sts;//注意 表示多的属性
}
方式二:二次查询
ClassesMapper.xml
<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="sts" javaType="list" ofType="Student" select="com.zw.mapper.StudentMapper.selectAll6" column="id" ></collection>
</resultMap>
<select id="selectAll" resultMap="BASE_RESULT_MAP">
select id ,name from classes
</select>
StudentMapper.xml
<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<result column="phone" property="phone"/>
<result column="class_id" property="classId"/>
<result column="className" property="className"/>
</resultMap>
<!-- 根据班级查询学生 -->
<select id="selectAll" resultMap="BASE_RESULT_MAP">
select <include refid="BASE_ALL_COLUMN" /> from student where class_id = #{classId}
</select>
3.11.mybatis中缓存
在mybatis缓存分为2类,一类是一级缓存,一类是二级缓存。一级缓存是指使用同一个SqlSession,二级缓存是指使用同一个SqlSessionFactory.默认Mybatis是开启一级缓存。即使用同一个SqlSession,一样sql语句,只需要访问数据库一次。
一级缓存:当使用同一个SqlSession,一样的sql只会执行一次。
public static void main(String[] args) {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> selectAll = studentMapper.selectAll();
System.out.println("25行"+selectAll);
selectAll = studentMapper.selectAll();
System.out.println("27行"+selectAll);
selectAll = studentMapper.selectAll();
System.out.println("29行:"+selectAll);
sqlSession.commit();
sqlSession.close();
}
二级缓存:SqlSessionFactory,二级缓存需要手动开启设置
-
在mapper映射文件中加上:
<!-- 开启二级缓存 --> <cache></cache>
-
查询实体要支持序列化