下面这个代码是使用JDBC实现基于id查询员工信息,我们来分析分析有什么弊端。
public Employee selectById(Long id) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement("select * from employee where id = ?");
ps.setLong(1,id);
rs = ps.executeQuery();
if (rs.next()){
Employee employee = new Employee();
employee.setId(rs.getLong("id"));
employee.setName(rs.getString("name"));
employee.setAge(rs.getInt("age"));
employee.setSex(rs.getInt("sex"));
employee.setPhone(rs.getString("phone"));
employee.setAddress(rs.getString("address"));
employee.setCreatedate(rs.getTimestamp("createdate"));
return employee;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(conn,ps,rs);
}
return null;
}
弊端如下:
1、SQL代码耦合在Java代码中。
2、SQL中的参数需要自己手动设置,获得结果集后需要自己进行结果的封装。
3、每次需要自己获取连接,用完以后关闭连接。
而这些问题,都可以使用mybatis框架来解决。
MyBatis官网英文版:https://mybatis.org/mybatis-3
MyBatis官网中文版:https://mybatis.org/mybatis-3/zh/index.html
MyBatis整合Spring:http://mybatis.org/spring/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
同时MyBatis也是一款半ORM映射的框架。ORM-对象关系映射。
提前准备数据库表
库名:mytest
表名:user
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`password` varchar(100) DEFAULT NULL,
`username` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.21version>
dependency>
咱们使用从 XML 中构建 SqlSessionFactory对象的方式。
① 准备配置文件:在resources
目录中新建mybatis核心配置文件mybatis-config.xml
,内容如下:
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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mytest?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
configuration>
② 读取配置文件,创建SqlSessionFactory
对象
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建Sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
③ 还可以将数据库连接参数(driver,url,username,password)抽离出去,职责单一化。
在resources
目录中新建db.propertis
内容如下:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mytest?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username=root
password=123456
mybatis-config.xml
内容修改如下:
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties">properties>
<environments default="development">
<environment id="development">
<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>
configuration>
① 创建User实体类
package cn.mybatis.domain;
public class User {
private Integer id;
private String password;
private String username;
//省略 get set toString
}
② 编写映射文件Mapper.xml,将SQL语句抽离到映射文件中。
在resources
目录新建mapper
目录,在其中创建UserMapper.xml
即:resources/mapper/UserMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.UserMapper">
<select id="selectById" parameterType="int" resultType="cn.mybatis.domain.User">
select * from user where id = #{id}
select>
mapper>
③ 加载UserMapper.xml
文件
在 mybatis-config.xml
中添加配置,加载mapper映射文件
<mappers>
<mapper resource="mapper/UserMapper.xml">mapper>
mappers>
④ 使用SqlSession执行sql
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.执行sql
// 参数1:定位sql,命名空间.id
// 参数2:执行sql时的参数
User user = session.selectOne("cn.mybatis.mapper.UserMapper.selectById", 1);
System.out.println(user);
//5.关闭会话
session.close();
① 映射文件编写sql
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.UserMapper">
<insert id="insert" parameterType="cn.mybatis.pojo.User">
insert into user(username,password) values(#{username},#{password})
insert>
mapper>
② 执行sql
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.执行sql
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
// 参数1:定位sql,命名空间.id
// 参数2:执行sql时的参数
session.insert("cn.mybatis.mapper.UserMapper.insert",user);
//5.提交事务(mybatis需要手动提交事务)
session.commit();
//6.关闭会话
session.close();
① 映射文件编写sql
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.UserMapper">
<update id="update" parameterType="cn.mybatis.domain.User">
update user set username=#{username},password=${password} where id = #{id}
update>
mapper>
② 执行sql
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.执行sql
User user = new User();
user.setId(1);
user.setUsername("张三");
user.setPassword("123456");
// 参数1:定位sql,命名空间.id
// 参数2:执行sql时的参数
session.update("cn.mybatis.mapper.UserMapper.update",user);
//5.提交事务(mybatis需要手动提交事务)
session.commit();
//6.关闭会话
session.close();
① 映射文件编写sql
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.UserMapper">
<delete id="delete" parameterType="int">
delete from user where id = #{id}
delete>
mapper>
② 执行sql
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.执行sql
// 参数1:定位sql,命名空间.id
// 参数2:执行sql时的参数
session.delete("cn.mybatis.mapper.UserMapper.delete",1);
//5.提交事务(mybatis需要手动提交事务)
session.commit();
//6.关闭会话
session.close();
① 映射文件编写sql
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.UserMapper">
<select id="selectAll" resultType="cn.mybatis.domain.User">
select * from user
select>
mapper>
② 执行sql
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.执行sql
// 参数1:定位sql,命名空间.id
List<Object> userList = session.selectList("cn.mybatis.mapper.UserMapper.selectAll");
System.out.println(userList);
//5.关闭会话
session.close();
经过以上的CRUD练习,我们可以体会到,在mybatis中sql参数的设置和结果集的封装都不需要我们手动完成,简化了编码步骤,但是,可以看出,咱们代码量貌似并没有随之减少。 不用担心,mybatis提供了映射器的方式,可以简化代码。
MyBatis映射器允许我们只为其提供接口以及相关抽象方法,还有Mapper映射文件,MyBatis就能够使用代理模式为我们进行具体的实现,从而完成数据库操作。
接口命名规则:XxxxMapper
package cn.mybatis.mapper;
...
public interface UserMapper {
//增
void insert(User user);
//删
void delete(Integer id);
//改
void update(User user);
//基于id查询
User selectById(Integer id);
//查询全部
List<User> selectAll();
}
映射文件mapper/UserMapper.xml
,核心配置文件mybatis-config.xml不用修改
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.UserMapper">
<insert id="insert" parameterType="cn.mybatis.domain.User">
insert into user(username,password) values(#{username},#{password})
insert>
<delete id="delete" parameterType="int">
delete from user where id = #{id}
delete>
<update id="update" parameterType="cn.mybatis.domain.User">
update user set username=#{username},password=${password} where id = #{id}
update>
<select id="selectById" parameterType="int" resultType="cn.mybatis.pojo.User">
select * from user where id = #{id}
select>
<select id="selectAll" resultType="cn.mybatis.domain.User">
select * from user
select>
mapper>
public class MyBatisTest1 {
@Test //新增
public void t1() throws IOException {
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.获取映射器(mapper接口)实现类对象(代理模式实现)
UserMapper mapper = session.getMapper(UserMapper.class);
//5.执行sql
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
mapper.insert(user);
//5.提交事务(mybatis需要手动提交事务)
session.commit();
//5.关闭会话
session.close();
}
@Test //删除
public void t2() throws IOException {
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.获取映射器(mapper接口)实现类对象(代理模式实现)
UserMapper mapper = session.getMapper(UserMapper.class);
//5.执行sql
mapper.delete(11);
//6.提交事务(mybatis需要手动提交事务)
session.commit();
//7.关闭会话
session.close();
}
@Test //修改
public void t3() throws IOException {
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.获取映射器(mapper接口)实现类对象(代理模式实现)
UserMapper mapper = session.getMapper(UserMapper.class);
//5.执行sql
User user = new User();
user.setId(12);
user.setUsername("李四");
user.setPassword("123456");
mapper.update(user);
//6.提交事务(mybatis需要手动提交事务)
session.commit();
//7.关闭会话
session.close();
}
@Test //基于id查询
public void t4() throws IOException {
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.获取映射器(mapper接口)实现类对象(代理模式实现)
UserMapper mapper = session.getMapper(UserMapper.class);
//5.执行查询
User user = mapper.selectById(12);
System.out.println(user);
//6.关闭会话
session.close();
}
@Test //查询全部
public void t5() throws IOException {
//1.读取mybatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sql会话工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
//3.创建sql会话对象
SqlSession session = sqlSessionFactory.openSession();
//4.获取映射器(mapper接口)实现类对象(代理模式实现)
UserMapper mapper = session.getMapper(UserMapper.class);
//5.执行查询
List<User> users = mapper.selectAll();
System.out.println(users);
//6.关闭会话
session.close();
}
}
对于UserMapper 这样的映射器类来说,还有另一种方法来完成语句映射。 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。
public interface UserMapper {
//基于id查询
@Select("select * from user where id = #{id}")
User selectById(Integer id);
...
}
类型别名(参数类型或返回值类型)可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
在核心配置文件mybatis-config.xml
中增加自定义别名配置。
<typeAliases>
<package name="cn.mybatis.domain"/>
typeAliases>
② 执行新增,并获取id
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUsername("tom");
user.setPassword("123456");
//---------新增前 没有id---------
System.out.println("新增前:"+user);
mapper.insert(user);
session.commit();
//---------新增后 有id---------
System.out.println("新增后:"+user);
session.close();
Mybatis 通过使用内置的日志工厂提供日志功能。支持如下日志方式:
咱们使用 Log4j 的方式打印mybatis的日志:
① pom.xml 中添加依赖
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
② 在resources目录
下新增配置文件:log4j.properties
名字必须一致
#设置日志级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG、TRACE
log4j.rootLogger=DEBUG, myLog
#设置对谁打印日志
## 表示对cn.mybatis.mapper.UserMapper打印日志
log4j.logger.cn.mybatis.mapper.UserMapper=DEBUG
## log4j.logger.cn.mybatis.mapper=DEBUG 表示对cn.mybatis.mapper包下的所有类都打印日志
#日志打印到控制台
log4j.appender.myLog=org.apache.log4j.ConsoleAppender
#使用自定义布局
log4j.appender.myLog.layout=org.apache.log4j.PatternLayout
#设置输出内容格式
# %d 表示当前系统时间
# %t 执行该业务的线程名
# %p 日志级别, 5表示输出字符的个数
# %c 表示指定业务所在的类的完全限定名(包名.类名)
# %m -- 输出额外信息,%n -- 表示换行
log4j.appender.myLog.layout.ConversionPattern=%d [%t] %5p [%c] - %m%n
默认情况下,mybatis使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement参数占位符,并通过占位符安全地设置参数(就像JDBC中使用 ? 一样)。 这样做更安全,更迅速,通常也是首选做法,不过有时你就是想直接在 SQL 语句中直接插入一个不转义的字符串。 比如 ORDER BY 子句,这时候你可以使用 ${}作为占位符,例如:ORDER BY ${columnName},此时,MyBatis 会通过创建 Statement对象来完成。
咱们通过日志打印,来直观的看看 #{} 和 ${} 的区别。
① 使用 #{}
<insert id="insert" parameterType="cn.mybatis.domain.User">
insert into user(username,password) values(#{username},#{password})
insert>
总结:#{}占位符
其底层使用PreparedStatement
和?号
,能够防止sql注入,安全。
② 使用 ${}
<insert id="insert" parameterType="cn.mybatis.domain.User">
insert into user(username,password) values(${username},${password})
insert>
日志如下,会发现报错了,原因是直接替换参数,缺少了引号。
[cn.mybatis.mapper.UserMapper.insert] - ==> Preparing: insert into user(username,password) values(root,123456)
[cn.mybatis.mapper.UserMapper.insert] - ==> Parameters:
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'root' in 'field list'
想要插入成功,就需要自己根据数据类型来加引号。
<insert id="insert" parameterType="cn.mybatis.domain.User">
insert into user(username,password) values('${username}','${password}')
insert>
总结:${}
占位符本质就是字符串拼接,使用Statement
完成,会有sql注入的风险,不安全。但是在一些特殊情况下,例如:ORDER BY ${字段名}
或者 GROUP BY ${字段名}
等场景就可以使用${}
占位符。
准备数据
CREATE TABLE `blog` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`author` varchar(255) DEFAULT NULL,
`content` varchar(255) DEFAULT NULL,
`state` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `blog` VALUES (1, '学Java', 'msk', '学学学学学学学学', 'active');
INSERT INTO `blog` VALUES (2, '学php', '张三', '习习习习习习', 'active');
INSERT INTO `blog` VALUES (3, '学JS', '王五', '学习学习学习学习学习', 'disable');
INSERT INTO `blog` VALUES (4, '学py', '王五', '入门到放弃', 'disable');
实体类
public class Blog {
private Long id;
private String title;
private String author;
private String content;
private String state;
//省略 get set toString
}
BlogMapper接口
public interface BlogMapper {
/**
* 根据条件查询博客
* @param blog
* @return
*/
List<Blog> selectBlog(Blog blog);
}
BlogMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from blog
where state = 'active'
<if test="title!=null and title!=''">
and title = #{title}
if>
<if test="author!=null and author!=''">
and author = #{author}
if>
select>
mapper>
在update语句中也适用,来看这样一段伪代码:
<update id="update" parameterType="User">
update user set username=#{username}
<if test="password!=null and password!=''">
,password=${password}
if>
where id = #{id}
update>
if语句能够通过判断实现动态sql,但是如果出现下面场景,可能就会出现问题,看代码:
<select id="selectBlog" resultType="Blog">
select * from blog
where
<if test="state!=null and state!=''">
state = #{state}
if>
<if test="title!=null and title!=''">
and title = #{title}
if>
<if test="author!=null and author!=''">
and author = #{author}
if>
select>
如果此时,3个条件都不传,就是出现这样的sql:
select * from blog where
如果只传了title
,是这样的sql:
select * from blog where and title =?
为了解决这个问题,mybatis提供了where标签,使用方式如下:
<select id="selectBlog" resultType="Blog">
select * from blog
<where>
<if test="state!=null and state!=''">
state = #{state}
if>
<if test="title!=null and title!=''">
and title = #{title}
if>
<if test="author!=null and author!=''">
and author = #{author}
if>
where>
select>
这里咱们使用两个案例来讲解foreach标签。
案例1:批量删除
public interface BlogMapper {
//批量删除
void deleteBatch(List<Long> ids);
}
SQL编写:
<!--
批量删除语法:delete from blog where id in (xx,xx,xx)
collection:被遍历的是谁。
需要注意:针对list和array,mybatis将他们封装为了map,key分别为:list和array,value就是传入的实参
所以,collection="list"就是获取list的值,collection="array"就是获取数组的值,而不是collection="ids"
item:遍历的当前项
open:开始符号
separator:分隔符
close:结束符号
-->
<delete id="deleteBatch">
delete from blog where id in
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
案例2:批量插入
public interface BlogMapper {
//批量插入
void insertBatch(List<Blog> blogs);
}
SQL编写:
<insert id="insertBatch">
insert into blog(title,author,content,state)
values
<foreach collection="list" item="blog" separator=",">
(#{blog.title},#{blog.author},#{blog.content},#{blog.state})
foreach>
insert>
以修改为例,咱们来学习一下set元素,set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
public interface BlogMapper {
//修改博客
void updateBlog(Blog blog);
}
SQL编写:
<update id="updateBlog">
update blog
<set>
<if test="title!=null">
title = #{title},
if>
<if test="author!=null">
author = #{author},
if>
<if test="content!=null">
content = #{content},
if>
<if test="state!=null">
state = #{state}
if>
set>
where id = #{id}
update>
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
public interface BlogMapper {
//基于条件查询博客
List<Blog> findActiveBlog(Blog blog);
}
SQL编写:
<select id="findActiveBlog" resultType="Blog">
select * from blog WHERE state = 'active'
<choose>
<when test="title != null">
AND title = #{title}
when>
<when test="content != null">
AND content = #{content}
when>
<otherwise>
AND author = 'msk'
otherwise>
choose>
select>
trim 元素可以用来定制自己的SQL。他包含4个属性:prefix - 前缀,prefixOverrides - 前置覆盖,suffix - 后缀,suffixOverrides - 后置覆盖。
案例1:查询中使用trim
<select id="selectBlog" resultType="Blog">
select * from blog
<where>
<if test="state!=null and state!=''">
state = #{state}
if>
<if test="title!=null and title!=''">
and title = #{title}
if>
<if test="author!=null and author!=''">
and author = #{author}
if>
where>
select>
<select id="selectBlog" resultType="Blog">
select * from blog
<trim prefix="where" prefixOverrides="and | or">
<if test="state!=null and state!=''">
state = #{state}
if>
<if test="title!=null and title!=''">
and title = #{title}
if>
<if test="author!=null and author!=''">
and author = #{author}
if>
trim>
select>
案例2:修改中使用trim
<update id="updateBlog">
update blog
<set>
<if test="title!=null">
title = #{title},
if>
<if test="author!=null">
author = #{author},
if>
<if test="content!=null">
content = #{content},
if>
<if test="state!=null">
state = #{state}
if>
set>
where id = #{id}
update>
<update id="updateBlog">
update blog
<trim prefix="set" suffixOverrides=",">
<if test="title!=null">
title = #{title},
if>
<if test="author!=null">
author = #{author},
if>
<if test="content!=null">
content = #{content},
if>
<if test="state!=null">
state = #{state}
if>
trim>
where id = #{id}
update>
<update id="updateBlog">
update blog
<trim prefix="set" suffixOverrides="," suffix="where id = #{id}">
<if test="title!=null">
title = #{title},
if>
<if test="author!=null">
author = #{author},
if>
<if test="content!=null">
content = #{content},
if>
<if test="state!=null">
state = #{state}
if>
trim>
update>
使用sql元素,可以抽取sql片段。
<sql id="columnName">
id,title,author,content,state
sql>
<select id="selectAll" resultType="Blog">
select <include refid="columnName"/> from blog
select>
准备测试数据
-- 部门表
CREATE TABLE `department` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `department` VALUES (1, '研发部');
INSERT INTO `department` VALUES (2, '产品部');
INSERT INTO `department` VALUES (3, '测试部');
-- 员工表
CREATE TABLE `employee` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`dept_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `employee` VALUES (1, '张三', 2);
INSERT INTO `employee` VALUES (2, '李四', 1);
INSERT INTO `employee` VALUES (3, '王五', 1);
INSERT INTO `employee` VALUES (4, '赵六', 3);
对应实体类
public class Department {
private Long id;
private String name;
//get set toString...
}
public class Employee{
private Long id;
private String name;
//get set toString...
}
咱们以部门表为例来演示自定义映射ResultMap,假设实体类中部门名称不是name
,而是myname
,进行查询后,会怎么样呢?部门名称会被查询出来吗?
package cn.mybatis.mapper;
public interface DepartmentMapper {
//查询部门
List<Department> selectAll();
}
DepartmentMapper.xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.DepartmentMapper">
<select id="selectAll" resultType="Department">
select * from department
select>
mapper>
使用自定义映射ResultMap来实现部门名称的查询。
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.mybatis.mapper.DepartmentMapper">
<select id="selectAll" resultMap="departmentMap">
select * from department
select>
<resultMap id="departmentMap" type="Department">
<id column="id" property="id">id>
<result column="name" property="myname">result>
resultMap>
mapper>
从部门的角度出发,一个部门可以包含多个员工,即:1对多。
需求:查询出部门信息,以及每个部门下的员工信息。
① SQL编写
SELECT d.*,e.id,e.name FROM department d
LEFT JOIN employee e
ON d.id = e.dept_id
② 对实体类进行改造
public class Employee {
private Long id; //员工id
private String name; //员工姓名
// get set toString...
}
public class Department {
private Long id; //部门id
private String name; //部门名称
private List<Employee> employees;//部门下的员工
// get set toString...
}
③ mapper接口
public interface DepartmentMapper {
//查询部门和部门下的员工信息
List<Department> selectDepartmentAndEmp();
}
④ mapper映射文件:DepartmentMapper.xml
<select id="selectDepartmentAndEmp" resultMap="deptMap">
SELECT d.*,e.id,e.name FROM department d LEFT JOIN employee e ON d.id = e.dept_id
select>
<resultMap id="deptMap" type="Department">
<id column="id" property="id">id>
<result column="name" property="name">result>
<collection property="employees" ofType="Employee">
<id column="id" property="id">id>
<result column="name" property="name">result>
collection>
resultMap>
会发现这样查的话,因为恰巧部门和员工都有id和name属性,会导致查询结果是:
Department{id=1, name='研发部', employees=[Department{id=1, name='研发部'}]}
Department{id=2, name='产品部', employees=[Department{id=2, name='产品部'}]}
Department{id=3, name='测试部', employees=[Department{id=3, name='测试部'}]}
解决办法是:可以为员工的字段名设置别名。
<select id="selectDepartmentAndEmp" resultMap="deptMap">
SELECT d.*,e.id eid,e.name ename FROM department d LEFT JOIN employee e ON d.id = e.dept_id
select>
<resultMap id="deptMap" type="Department">
<id column="id" property="id">id>
<result column="name" property="name">result>
<collection property="employees" ofType="Employee">
<id column="eid" property="id">id>
<result column="ename" property="name">result>
collection>
resultMap>
最终查询结果为:
Department{id=1, name='研发部', employees=[Department{id=2, name='李四'}, Department{id=3, name='王五'}]}
Department{id=2, name='产品部', employees=[Department{id=1, name='张三'}]}
Department{id=3, name='测试部', employees=[Department{id=4, name='赵六'}]}
<select id="selectDepartmentAndEmp" resultMap="deptMap">
SELECT * FROM department
select>
<resultMap id="deptMap" type="Department">
<id column="id" property="id">id>
<result column="name" property="name">result>
<collection property="employees" ofType="Employee" select="selectEmp" column="id">collection>
resultMap>
<select id="selectEmp" resultType="Employee">
select * from employee where dept_id = #{deptId}
select>
从员工的角度出发,多个员工隶属于一个部门,即:多对一。
需求:查询员工信息,以及每个员工所属部门信息。
① SQL编写
SELECT e.id,e.name,d.id did,d.name dname FROM employee e
LEFT JOIN department d
ON e.dept_id=d.id
② 对实体类进行改造
public class Employee {
private Long id; //员工id
private String name; //员工姓名
private Department department; //员工所属部门
// get set toString...
}
public class Department {
private Long id; //部门id
private String name; //部门名称
//get set toString
}
③ mapper接口
public interface EmployeeMapper {
//查询员工和对应的部门信息
List<Employee> selectEmployeeAndDept();
}
④ mapper映射文件:EmployeeMapper.xml
<select id="selectEmployeeAndDept" resultMap="EmployeeMap">
SELECT e.id,e.name,d.id did,d.name dname FROM employee e
LEFT JOIN department d
ON e.dept_id=d.id
select>
<resultMap id="EmployeeMap" type="Employee">
<id column="id" property="id">id>
<result column="name" property="name">result>
<association property="department" javaType="Department">
<id column="did" property="id">id>
<result column="dname" property="name">result>
association>
resultMap>
<select id="selectEmployeeAndDept" resultMap="EmployeeMap">
SELECT * FROM employee
select>
<resultMap id="EmployeeMap" type="Employee">
<id column="id" property="id">id>
<result column="name" property="name">result>
<association property="department" javaType="Department" select="selectDepartmentById" column="dept_id">association>
resultMap>
<select id="selectDepartmentById" resultType="Department">
SELECT * FROM department where id = #{id}
select>
mybatis为了提高查询效率,使用缓存的方式将查询结果缓存到内存中,当相同的查询执行第二次时,并不会真实的执行数据库查询,而是从缓存中获取数据返回。mybatis提供了两种缓存方式。
一级缓存:也称为SqlSession(会话)级别的缓存,用于保存用户在一次会话过程中查询的结果,如果用户使用同一个SqlSession执行了两次相同的查询,第二次则会使用缓存中的数据,而不会执行数据库查询,同时,一级缓存是自动开启的。
@Test
public void testselectByPrimaryKey() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sqlSessionFactory.openSession();
BlogMapper mapper = session.getMapper(BlogMapper.class);
//第一次查询
Blog blog1 = mapper.selectByPrimaryKey(4L);
System.out.println(blog1);
//第二次查询
Blog blog2 = mapper.selectByPrimaryKey(4L);
System.out.println(blog2);
session.close();
}
但是由于每个SqlSession有自己的缓存,这就会导致出现脏数据的问题。
二级缓存:也称为全局缓存或者跨会话级别的缓存。可以实现不同的SqlSession执行同一个Mapper(namespace命名空间)下的查询SQL时,相同查询第二次就会从缓存中获取返回,而不会真正的执行数据库查询。
默认情况下是没有开启二级缓存的,需要手动开启。
1.在核心配置文件mybatis-config.xml
中加入:
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
2.在mapper映射文件中加入:
<mapper namespace="...">
...
<cache/>
...
mapper>
3.测试看结果
@Test
public void testselectByPrimaryKey() throws IOException {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session1 = sqlSessionFactory.openSession();
BlogMapper mapper1 = session1.getMapper(BlogMapper.class);
//第一个SqlSession查询
Blog blog1 = mapper1.selectByPrimaryKey(4L);
System.out.println(blog1);
session1.close();
//第二个SqlSession查询
SqlSession session2 = sqlSessionFactory.openSession();
BlogMapper mapper2 = session2.getMapper(BlogMapper.class);
Blog blog2 = mapper2.selectByPrimaryKey(4L);
System.out.println(blog2);
session2.close();
}