MyBatis简介(了解)
什么是MyBatis
MyBatis 本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。
下图是MyBatis架构图:
(1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
(2)基于SqlSessionFactory可以生成SqlSession对象
(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
(4)Executor是SqlSession底层的对象,用于执行SQL语句
(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)
为什么要使用MyBatis
思考:在开始之前,思考下如何通过JDBC查询Emp表中的所有记录,并封装到一个List集合中返回。(演示:准备数据、导包、导入JDBC程序)
1、使用传统方式JDBC访问数据库:
(1)使用JDBC访问数据库有大量重复代码(比如注册驱动、获取连接、获取传输器、释放资源等);
(2)JDBC自身没有连接池,会频繁的创建连接和关闭连接,效率低;
(3)SQL是写死在程序中,一旦修改SQL,需要对类重新编译;
(4)对查询SQL执行后返回的ResultSet对象,需要手动处理,有时会特别麻烦;
...
2、使用mybatis框架访问数据库:
(1)Mybatis对JDBC对了封装,可以简化JDBC代码;
(2)Mybatis自身支持连接池(也可以配置其他的连接池),因此可以提高程序的效率;
(3)Mybatis是将SQL配置在mapper文件中,修改SQL只是修改配置文件,类不需要重新编译。
(4)对查询SQL执行后返回的ResultSet对象,Mybatis会帮我们处理,转换成Java对象。
...
总之,JDBC中所有的问题(代码繁琐、有太多重复代码、需要操作太多对象、释放资源、对结果的处理太麻烦等),在Mybatis框架中几乎都得到了解决!!
MyBatis快速入门
准备数据,创建库和表
创建test库、emp表,并插入若干条记录
1
-- 1、创建数据库 test 数据库
2
create database if not exists test charset utf8;
3
use test ; -- 选择yonghedb数据库
4
-- 2、删除emp表(如果存在)
5
drop table if exists emp;
6
-- 3、在 test 库中创建 emp 表
7
create table emp(
8
id int primary key auto_increment,
9
name varchar(50),
10
job varchar(50),
11
salary double
12
);
13
-- 4、往 emp 表中, 插入若干条记录
14
insert into emp values(null, '张三', '程序员', 3300);
15
insert into emp values(null, '李四', '程序员', 2800);
16
insert into emp values(null, '王五', '程序员鼓励师', 2700);
17
insert into emp values(null, '王二', '部门总监', 4200);
18
insert into emp values(null, '麻子', '程序员', 3000);
19
insert into emp values(null, '最帅三太子', '程序员', 3500);
20
insert into emp values(null, '苍老师', '程序员', 3700);
21
insert into emp values(null, '波多野结衣', 'CEO', 5000);
创建工程,导入所需jar包、创建测试类
1、创建Maven的java工程
2、导入junit、mysql、mybaits、log4j等开发包
在pom.xml文件中引入相关依赖包即可
1
<dependencies>
2
<!-- junit单元测试 -->
3
<dependency>
4
<groupId>junit</groupId>
5
<artifactId>junit</artifactId>
6
<version>4.9</version>
7
</dependency>
8
<!-- mysql驱动 -->
9
<dependency>
10
<groupId>mysql</groupId>
11
<artifactId>mysql-connector-java</artifactId>
12
<version>5.1.32</version>
13
</dependency>
14
<!-- mybatis -->
15
<dependency>
16
<groupId>org.mybatis</groupId>
17
<artifactId>mybatis</artifactId>
18
<version>3.2.8</version>
19
</dependency>
20
<!-- 整合log4j -->
21
<dependency>
22
<groupId>org.slf4j</groupId>
23
<artifactId>slf4j-log4j12</artifactId>
24
<version>1.6.4</version>
25
</dependency>
26
</dependencies>
3、创建com.tedu.mybatis.TestMybatis01测试类,并提供findAll方法(查询emp表中所有的员工信息),开发步骤如下:
1
/** 练习1(快速入门): 查询emp表中的所有员工, 返回一个List<Emp>集合
2
* @throws IOException */
3
@Test
4
public void findAll() throws IOException {
5
//1.读取mybatis的核心配置文件(mybatis-config.xml)
6
//2.通过配置信息获取一个SqlSessionFactory工厂对象
7
//3.通过工厂获取一个SqlSession对象
8
//4.通过namespace+id找到要执行的sql语句并执行sql语句
9
//5.输出结果
10
}
添加mybatis-config.xml文件
1、在src/main/resources目录下,创建mybatis-config.xml文件(MyBatis的核心配置文件)
2、mybatis-config.xml文件配置如下:
mybatis-config文件头信息如下:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE configuration
3
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-config.dtd">
5
6
<!-- MyBatis的全局配置文件 -->
7
<configuration >
8
9
</configuration>
mybatis-config文件详细配置如下:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE configuration
3
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-config.dtd">
5
6
<!-- MyBatis的全局配置文件 -->
7
<configuration >
8
<!-- 1.配置环境,可配置多个环境(比如:develop开发、test测试) -->
9
<environments default="develop">
10
<environment id="develop">
11
12
<!-- 1.1.配置事务管理方式:JDBC/MANAGED
13
JDBC:将事务交给JDBC管理(推荐)
14
MANAGED:自己管理事务
15
-->
16
<transactionManager type="JDBC"></transactionManager>
17
18
<!-- 1.2.配置数据源,即连接池 JNDI/POOLED/UNPOOLED
19
JNDI:已过时
20
POOLED:使用连接池(推荐)
21
UNPOOLED:不使用连接池
22
-->
23
<dataSource type="POOLED">
24
<property name="driver" value="com.mysql.jdbc.Driver"/>
25
<property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/>
26
<property name="username" value="root"/>
27
<property name="password" value="root"/>
28
</dataSource>
29
</environment>
30
</environments>
31
32
<!-- 2.导入Mapper配置文件,如果mapper文件有多个,可以通过多个mapper标签导入 -->
33
<mappers>
34
<mapper resource="EmpMapper.xml"/>
35
</mappers>
36
</configuration>
添加EmpMapper.xml文件
1、在src/main/resources目录下,创建EmpMapper.xml文件 (实体类的映射文件)
2、EmpMapper.xml文件配置如下:
EmpMapper文件头信息如下:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE mapper
3
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
6
<!--
7
不同Mapper文件的namespace值应该保证唯一
8
在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
9
-->
10
<mapper namespace="">
11
12
13
</mapper>
EmpMapper文件详细配置如下:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE mapper
3
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
6
<!--
7
不同Mapper文件的namespace值应该保证唯一
8
在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
9
-->
10
<mapper namespace="EmpMapper">
11
<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
12
<!-- 练习1: 查询emp表中的所有员工信息
13
resultType指定查询的结果将会封装到什么类型中
14
即使最终返回的结果是集合(List<Emp>),resultType也只需要指定集合中的泛型即可!
15
-->
16
<select id="findAll" resultType="com.tedu.pojo.Emp">
17
select * from emp
18
</select>
19
20
</mapper>
添加并编写Emp实体类
注意:在当前实例中,Emp类中的属性和数据库表的字段名称必须一致,否则将会无法将结果集封装到Java对象中。
在src/main/java目录下创建 com.tedu.pojo.Emp类,并编辑Emp类:提供私有属性以及对应的getter方法、setter方法,并重写toString方法
1
package com.tedu.pojo;
2
/**
3
* 实体类,用于封装Emp表中的一条用户信息
4
*/
5
public class Emp {
6
//1.声明实体类中的属性
7
private Integer id;
8
private String name;
9
private String job;
10
private Double salary;
11
12
//2.提供对应的getter和setter方法
13
public Integer getId() {
14
return id;
15
}
16
public void setId(Integer id) {
17
this.id = id;
18
}
19
public String getName() {
20
return name;
21
}
22
public void setName(String name) {
23
this.name = name;
24
}
25
public String getJob() {
26
return job;
27
}
28
public void setJob(String job) {
29
this.job = job;
30
}
31
public Double getSalary() {
32
return salary;
33
}
34
public void setSalary(Double salary) {
35
this.salary = salary;
36
}
37
38
//3.重写toString方法
39
@Override
40
public String toString() {
41
return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]";
42
}
43
}
实现测试类,并测试
1、实现findAll方法,代码如下:
1
/** 练习1(快速入门): 查询emp表中的所有员工, 返回一个List<Emp>集合
2
* @throws IOException */
3
@Test
4
public void findAll() throws IOException {
5
//1.读取mybatis的核心配置文件(mybatis-config.xml)
6
InputStream in = Resources
7
.getResourceAsStream("mybatis-config.xml");
8
//2.通过配置信息获取一个SqlSessionFactory工厂对象
9
SqlSessionFactory fac =
10
new SqlSessionFactoryBuilder().build( in );
11
//3.通过工厂获取一个SqlSession对象
12
SqlSession session = fac.openSession();
13
//4.通过namespace+id找到要执行的sql语句并执行sql语句
14
List<Emp> list = session
15
.selectList("EmpMapper.findAll");
16
//5.输出结果
17
for(Emp e : list) {
18
System.out.println( e );
19
}
20
}
2、执行findAll方法,输出结果为:
就会遍历出来
MyBatis入门细节
mybatis-config.xml配置文件
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE configuration
3
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-config.dtd">
5
6
<!-- MyBatis的全局配置文件 -->
7
<configuration >
8
<!-- 1.配置环境,可配置多个环境(比如:develop开发、test测试) -->
9
<environments default="develop">
10
<environment id="develop">
11
12
<!-- 1.1.配置事务管理方式:JDBC/MANAGED
13
JDBC:将事务交给JDBC管理(推荐)
14
MANAGED:自己管理事务
15
-->
16
<transactionManager type="JDBC"></transactionManager>
17
18
<!-- 1.2.配置数据源,即连接池 JNDI/POOLED/UNPOOLED
19
JNDI:已过时
20
POOLED:使用连接池(推荐)
21
UNPOOLED:不使用连接池
22
-->
23
<dataSource type="POOLED">
24
<property name="driver" value="com.mysql.jdbc.Driver"/>
25
<property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/>
26
<property name="username" value="root"/>
27
<property name="password" value="root"/>
28
</dataSource>
29
</environment>
30
</environments>
31
32
<!-- 2.导入Mapper配置文件,如果mapper文件有多个,可以通过多个mapper标签导入 -->
33
<mappers>
34
<mapper resource="EmpMapper.xml"/>
35
</mappers>
36
</configuration>
environments
标签:该标签内部可以配置多个environment,即多种环境,每种环境可以做不同配置或连接不同数据库。例如,开发、测试、生产环境可能需要不同的配置,连接的数据库可能也不相同,因此我们可以配置三个environment,分别对应上面三种不同的环境。
但是要记住,environment可以配置多个,但是最终要使用的只能是其中一个!
environment
标签:内部可以配置多种配置信息,下面介绍事务管理配置和数据源配置。
transactionManage
标签:事务管理配置,mybatis中有两种事务管理方式,也就是
type="[JDBC|MANAGED]。
1
JDBC:这个配置就是直接使用了 JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。推荐使用。
2
MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。需要自己手动添加并管理。不推荐使用。
dataSource
标签:数据源,也就是连接池配置。这里type指定数据源类型,有三种内建的类型:JNDI、POOLED、UNPOOLED
1
JNDI:已过时,不推荐使用!
2
POOLED:使用连接池,mybatis会创建连接池,并从连接池中获取连接访问数据库,在操作完成后,将会把连接返回连池。
3
UNPOOLED:不使用连接池,该方式适用于只有小规模数量并发用户的简单应用程序上。
mappers
标签:用于导入mapper文件的位置,其中可以配置多个mapper,即可以导入多个mapper文件。
EmpMapper.xml配置文件
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE mapper
3
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
6
<!--
7
不同Mapper文件的namespace值应该保证唯一
8
在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
9
-->
10
<mapper namespace="EmpMapper">
11
<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
12
<!-- 练习1: 查询emp表中的所有员工信息
13
resultType指定查询的结果将会封装到什么类型中
14
即使最终返回的结果是集合(List<Emp>),resultType也只需要指定集合中的泛型即可!
15
-->
16
<select id="findAll" resultType="com.tedu.pojo.Emp">
17
select * from emp
18
</select>
19
20
</mapper>
(1)第1行是xml的文档声明,用于声明xml的版本和编码
(2)第2、3、4行,引入了xml约束文档,当前xml文档将会按照mybatis-3-mapper.dtd文件所要求的规则进行书写。
(3)Mapper标签:根标签,其中namespace(名称空间,也叫命名空间),要求不能重复。在程序中通过【namespace + id 】定位到要执行哪一条SQL语句
(4)select标签:用于指定将来要执行的各种SQL语句。标签上可以声明属性,下面介绍常用的属性:id、resultType、resultMap
- id属性:要求值不能重复。将来在执行SQL时,可以通过【namespace + id】找到指定SQL并执行。
- resultType属性:从这条SQL语句中返回所期望类型的类的完全限定名称(包名+类名)。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。
简而言之,resultType控制查询SQL执行后返回值的类型或集合中的泛型,例如查询emp表中的单条记录,返回值是一个Emp对象,因此,resultType="com.tedu.pojo.Emp";
如果查询emp表中的多条记录,返回值是一个List,此时resultType的值应该集合中的泛型,因此resultType="com.tedu.pojo.Emp"; - resultMap属性:复杂对象结构(例如多表关联查询等)。 使用 resultType 或 resultMap,但不能同时使用。
MyBatis增删改查
新增员工
1、编辑EmpMapper.xml文件, 添加新增员工对应的sql.
1
<!-- 练习2: 新增员工信息: 赵云 保安 6000
2
增删改的标签上不用指定resultType, 因为返回值都是int类型
3
-->
4
<update id="insert" >
5
insert into emp value(null, '赵云', '保安', 6000)
6
</update>
2、编写TestMybatis类,添加testInsert方法,实现新增员工操作。
1
/** 练习2: 新增员工信息: 赵云 保安 6000 */
2
@Test
3
public void testInsert() {
4
//执行sql语句, 返回执行结果
5
int rows = session.update("EmpMapper.insert");
6
//提交事务
7
session.commit();
8
System.out.println("影响的行数: "+rows);
9
}
修改员工
1、编辑EmpMapper.xml文件, 添加新增员工对应的sql。
1
<!-- 练习3:修改员工信息:赵云 保镖 20000 -->
2
<update id="update">
3
update emp set job='保镖', salary=20000 where name='赵云'
4
</update>
2、编写TestMybatis类,添加testUpdate方法,实现修改员工信息。
1
/** 练习3: 修改员工信息, 将赵云的job改为'保镖',salary改为20000 */
2
@Test
3
public void testUpdate() {
4
//执行sql语句, 返回执行结果
5
int rows = session.update("EmpMapper.update");
6
//提交事务
7
session.commit();
8
System.out.println("影响行数:"+rows);
9
}
删除员工
1、编辑EmpMapper.xml文件, 添加新增员工对应的sql。
1
<!-- 练习4: 删除name为'赵云'的记录 -->
2
<update id="delete">
3
delete from emp where name='赵云'
4
</update>
2、编写TestMybatis类,添加testDelete方法,实现删除员工。
1
/** 练习4: 删除name为'赵云'的记录 */
2
@Test
3
public void testDelete() {
4
//执行sql语句, 返回执行结果
5
int rows = session.update("EmpMapper.delete");
6
//提交事务
7
session.commit();
8
System.out.println("影响行数:"+rows);
9
}
mybatis中的占位符
#{}占位符
在上面的增删改查操作中,SQL语句中的值是写死在SQL语句中的,而在实际开发中,此处的值往往是用户提交过来的值,因此这里我们需要将SQL中写死的值替换为占位符。
在mybatis中占位符有两个,分别是 #{}
占位符 和 ${}
占位符:
#{}:
相当于JDBC中的问号(?)占位符,是为SQL语句中的参数值进行占位,大部分情况下都是使用#{}占位符;并且当#{}占位符是为字符串或者日期类型的值进行占位时,在参数值传过来替换占位符的同时,会进行转义处理(在字符串或日期类型的值的两边加上单引号);
1
在mapper文件中: select * from emp where name=#{name}
2
在程序执行时: select * from emp where name=?
3
参数:王海涛,将参数传入,替换占位符
4
select * from emp where name=王海涛; -- 错误
5
select * from emp where name=‘王海涛’; -- 正确${}
:是为SQL片段(字符串)进行占位,将传过来的SQL片段直接拼接在 ${} 占位符所在的位置,不会进行任何的转义处理。(由于是直接将参数拼接在SQL语句中,因此可能会引发SQL注入攻击问题)
需要注意的是:使用 ${} 占位符为SQL语句中的片段占位时,即使只有一个占位符,需要传的也只有一个参数,也需要将参数先封装再传递!
练习5:查询emp表中指定id的员工信息
在mapper文件中编写SQL语句:
1
<!-- 练习5: 查询emp表中指定id的员工信息 -->
2
<select id="findById" resultType="com.tedu.pojo.Emp">
3
select * from emp where id=#{id}
4
</select>
Java代码实现:
1
/** 练习5: 查询emp表中指定id的员工信息 */
2
@Test
3
public void testFindById() {
4
//执行sql语句, 返回执行结果
5
Emp emp = session.selectOne( "EmpMapper.findById", 1 );
6
System.out.println( emp );
7
}
练习6:新增员工信息: 张飞 Java开发工程师 15000
在mapper文件中编写SQL语句:
1
<!-- 练习6: 新增员工信息: 张飞 Java开发工程师 15000
2
如果通过map集合传输参数, 需要保证占位符中的变量名
3
和map集合中的key保持一致
4
如果通过pojo对象传输参数, 需要保证占位符中的变量名
5
和对象中的属性名保持一致, 或者在pojo中有对应的
6
getXxx方法
7
-->
8
<update id="insert2">
9
insert into emp values (null, #{name}, #{job}, #{salary})
10
</update>
Java代码实现:
1
/** 练习6: 新增员工信息: 张飞 Java开发工程师 15000 */
2
@Test
3
public void testInsert2() {
4
//将要传输的参数封装到map集合中
5
//Map map = new HashMap();
6
//map.put("name", "张飞");
7
//map.put("job", "Java开发工程师");
8
//map.put("salary", 15000);
9
//也可以将要传输的参数封装到Emp对象中
10
Emp emp = new Emp();
11
emp.setName("关羽123");
12
emp.setJob("保安");
13
emp.setSalary(8000.0);
14
//执行sql语句
15
intsession rows = session.update("EmpMapper.insert2", emp);
16
//提交事务
17
session.commit();
18
System.out.println( "影响的行数: "+rows );
19
}
练习7:修改员工信息: 张飞 架构师 25000
在mapper文件中编写SQL语句:
1
<!-- 练习7: 修改员工信息: 张飞 架构师 25000 -->
2
<update id="update2">
3
update emp set job=#{job}, salary=#{salary}
4
where name=#{name}
5
</update>
Java代码实现:
1
/** 练习7: 修改员工信息: 张飞 架构师 25000 */
2
@Test
3
public void testUpdate2() {
4
//将参数封装到Emp对象中
5
Emp emp = new Emp();
6
emp.setName("张飞");
7
emp.setJob("架构师");
8
emp.setSalary(25000.0);
9
//执行sql语句
10
intsession rows = session.update("EmpMapper.update2", emp);
11
//提交事务
12
session.commit();
13
System.out.println("影响的行数: "+rows);
14
}
练习8:删除emp表中指定id的员工信息
mapper文件配置:
1
<!-- 练习8:删除emp表中指定id的员工信息 -->
2
<insert id="delete2" parameterType="String">
3
delete from emp where id=#{id}
4
</insert>
java代码示例:
1
/* 练习8:删除emp表中指定id的员工信息 */
2
public void testDelete2() throws IOException{
3
......
4
//执行SQL语句
5
int rows = session.delete("EmpMapper.delete2", 1);
6
//提交事务
7
session.commit();
8
System.out.println("影响行数:"+rows);
9
}
在上面的增删改查练习中,当SQL语句中包含的参数值是传递过来的,在SQL语句中我们会通过#{}
占位符进行占位,在SQL语句真正执行时,再将传递过来的值替换SQL语句中的占位符。
其实,#{}
就是JDBC中的问号(?)占位符,因此为了安全考虑,在执行时会对传递过来的字符串和日期类型高的值进行转译处理。
例如:查询指定name的员工信息,SQL语句为:
select * from emp where name=#{name}
其实就等价于JDBC中: select * from emp where name=?
,
如果传过来的参数值为:王海涛,那么最终执行的SQL语句为:
1
-- 在参数替换占位符的同时进行了转义处理(在值的两边加上了单引号)
2
select * from emp where name='王海涛'
${}占位符
那么如果我们在传递的时候不是一个参数值,而是一个SQL片段呢?
例如:在查询时,我们想动态的传递查询的列:
1
select #{columns} from emp
此时传递过来的应该是一个SQL片段,不同于上面的参数值,如果此时还用#{}
,也会像上面一样被转译处理:select 'id,name,job' from emp
,这不是我们希望看到的!
如果不想让传过来的SQL片段被转译处理,而是直接拼接在SQL语句中,那么这里可以使用${}
,例如:
1
select ${columns} from emp
拼接之后:select id,name,job from emp
练习9:动态指定要查询的列
在mapper文件中编写SQL语句:
1
<!-- 练习9: 动态指定要显示的列 -->
2
<select id="findAll2" resultType="com.tedu.pojo.Emp">
3
select ${cols} from emp
4
</select>
java代码示例:
1
/** 练习9: 动态指定要查询的列 */
2
@Test
3
public void testFindAll2() {
4
Map map = new HashMap();
5
//map.put("cols", "id, name");
6
//map.put("cols", "id, name, salary");
7
map.put("cols", "id,name,job,salary");
8
//执行sql语句, 返回结果
9
List<Emp> list = session.selectList("EmpMapper.findAll2", map);
10
//输出结果
11
for ( Emp e : list ) {
12
System.out.println( e );
13
}
14
}
示例2: 根据name模糊查询emp表
在mapper文件中编写SQL语句:
1
<!-- 练习10: 根据name模糊查询emp表 -->
2
<select id="findAll3" resultType="com.tedu.pojo.Emp">
3
select * from emp
4
where name like '%${name}%'
5
</select>
1
<!-- 练习11: 根据name模糊查询emp表 -->
2
<select id="findAll4" resultType="com.tedu.pojo.Emp">
3
select * from emp
4
where name like #{name}
5
</select>
Java代码实现:
1
/**
2
* 练习10: 根据name模糊查询emp表
3
* '%王%' '%刘%'
4
*/
5
@Test
6
public void testFindAll3() {
7
//将参数封装到map集合中
8
Map map = new HashMap();
9
map.put("name", "涛");
10
//执行sql, 返回结果
11
List<Emp> list = session.selectList("EmpMapper.findAll3", map);
12
//输出结果
13
for (Emp emp : list) {
14
System.out.println( emp );
15
}
16
}
1
/**
2
* 练习11: 根据name模糊查询emp表
3
* '%王%' '%刘%'
4
*/
5
@Test
6
public void testFindAll4() {
7
//将参数封装到map集合中
8
Map map = new HashMap();
9
map.put("name", "%刘%");
10
//执行sql, 返回结果
11
List<Emp> list = session.selectList("EmpMapper.findAll4", map);
12
//输出结果
13
for (Emp emp : list) {
14
System.out.println( emp );
15
}
16
}
需要注意的是,在传递 ${}
对应的值时,即使只有一个参数,也需要将值存入map集合中!!
总结:在大多数情况下还是使用#{}占位符,而${}多用于为SQL片段进行占位!
动态SQL标签
if、where标签
<if>
标签:是根据test属性
中的布尔表达式的值,从而决定是否执行包含在其中的SQL片段。如果判断结果为true,则执行其中的SQL片段;如果结果为false,则不执行其中的SQL片段<where>
标签:用于对包含在其中的SQL片段进行检索,在需要时可以生成where关键字,并且在需要时会剔除多余的连接词(比如and或者or)
练习12:根据薪资查询员工信息(if标签)
<if>
标签:是根据test属性
中的布尔表达式的值,从而决定是否执行包含在其中的SQL片段。如果判断结果为true,则执行其中的SQL片段;如果结果为false,则不执行其中的SQL片段
在mapper文件中编写SQL语句:
1
<!-- 练习12: 根据薪资查询员工信息
2
* 如果没有参数, 则不执行where子句, 默认查询所有员工:
3
* select * from emp
4
* 如果参数中只有minSal(即minSal不为null), 则:
5
* ... where salary > minSal
6
* 如果参数中只有maxSal(即maxSal不为null), 则:
7
* ... where salary < maxSal
8
* 如果参数有 minSal、maxSal(即minSal、maxSal不为null), 则:
9
* ... where salary > minSal and salary < maxSal -->
10
<select id="findAllBySal" resultType="com.tedu.pojo.Emp">
11
select * from emp
12
where 1=1
13
<if test="minSal != null">
14
and salary>#{minSal}
15
</if>
16
<if test="maxSal != null">
17
and salary <![CDATA[ < ]]> #{maxSal}
18
</if>
19
</select>
Java代码实现:
1
/* 练习12: 根据薪资查询员工信息
2
* 如果没有参数, 则不执行where子句, 默认查询所有员工:
3
* select * from emp
4
* 如果参数中只有minSal(即minSal不为null), 则:
5
* ... where salary > minSal
6
* 如果参数中只有maxSal(即maxSal不为null), 则:
7
* ... where salary < maxSal
8
* 如果参数有 minSal、maxSal(即minSal、maxSal不为null), 则:
9
* ... where salary > minSal and salary < maxSal */
10
@Test
11
public void testFindBySal() {
12
Map<String, Object> map = new HashMap<String, Object>();
13
//map.put( "minSal" , 3000 );
14
//map.put( "maxSal", 4000 );
15
List<Emp> list = session.selectList( "EmpMapper.findBySal", map );
16
for (Emp emp : list) {
17
System.out.println( emp );
18
}
19
}
练习13:根据薪资查询员工信息(where标签)
<where>
标签:对包含在其中的SQL片段进行检索,在需要时可以生成WHERE关键字,并且在需要时会剔除多余的连接词(比如and或者or)
1
<!-- 练习13: 根据薪资查询员工信息
2
* 如果没有参数, 则不执行where子句, 默认查询所有员工:
3
* select * from emp
4
* 如果参数中只有minSal(即minSal不为null), 则:
5
* ... where salary > minSal
6
* 如果参数中只有maxSal(即maxSal不为null), 则:
7
* ... where salary < maxSal
8
* 如果参数有 minSal、maxSal(即minSal、maxSal不为null), 则:
9
* ... where salary > minSal and salary < maxSal -->
10
<select id="findAllBySal2" resultType="com.tedu.pojo.Emp">
11
select * from emp
12
<where>
13
<if test="minSal != null">
14
and salary>#{minSal}
15
</if>
16
<if test="maxSal != null">
17
and salary <![CDATA[ < ]]> #{maxSal}
18
</if>
19
</where>
20
</select>
Java代码实现:
1
/**
2
* 练习13: 根据薪资查询员工信息
3
* 如果没有参数, 则不执行where子句, 默认查询所有员工:
4
* select * from emp
5
* 如果参数中只有minSal(即minSal不为null), 则:
6
* ... where salary > minSal
7
* 如果参数中只有maxSal(即maxSal不为null), 则:
8
* ... where salary < maxSal
9
* 如果参数有 minSal、maxSal(即minSal、maxSal不为null), 则:
10
* ... where salary > minSal and salary < maxSal */
11
@Test
12
public void testFindAllBySal() {
13
//封装参数到map集合中
14
Map map = new HashMap();
15
map.put("minSal", 3000);
16
map.put("maxSal", 4000.0);
17
List<Emp> list = session.selectList("EmpMapper.findAllBySal2", map);
18
for (Emp emp : list) {
19
System.out.println( emp );
20
}
21
}
foreach标签
foreach
标签:可以对传过来的参数数组或集合进行遍历,以下是foreach标签上的各个属性介绍:
data-draft-node="block" data-draft-type="table" data-size="normal" data-row-style="normal">
属性属性描述练习14: 根据员工的id批量删除员工信息
在mapper文件中编写SQL语句:
1
<!-- 练习14: 根据员工的id批量删除员工信息
2
delete from emp where id in (1,3,5,7)
3
collection: 如果传的参数仅仅是一个数组或者List集合, collection可以指定为
4
array或list; 如果传的是多个参数,用map封装,collection则指定为map中的key
5
open: 指定生成的SQL片段以什么符号开始
6
close: 指定生成的SQL片段以什么符号结束
7
item: 指定变量接收数组或集合中的元素
8
separator: 指定一个间隔符, 在将数组或集合中的每个元素拼接到SQL片段之后,
9
在后面拼接一个间隔符
10
-->
11
<delete id="deleteByIds">
12
delete from emp where id in
13
<foreach collection="array" open="(" item="id" separator="," close=")">
14
#{id}
15
</foreach>
16
</delete>
Java代码实现:
1
/* 练习14: 根据员工的id批量删除员工信息 */
2
@Test
3
public void testDeleteByIds() {
4
//获取所要删除的员工的id数组
5
Integer[] ids = {1,3,5,7};
6
int rows = session.delete( "EmpMapper.deleteByIds", ids );
7
System.out.println( "影响行数: "+rows );
8
}
练习15:根据员工的id批量更新员工信息
在mapper文件中编写SQL语句:
1
<!-- 练习15: 根据员工的id批量更新员工信息
2
将id为 2、4、6、8的员工的薪资在原有基础上增加1000
3
update emp set salary=salary + 1000
4
where id in(2,4,6,8);
5
-->
6
<update id="updateByIds">
7
update emp set salary=salary + #{sal}
8
where id in
9
<foreach collection="arrIds" open="(" item="id" separator="," close=")">
10
#{id}
11
</foreach>
12
</update>
Java代码实现:
1
/* 练习15: 根据员工的id批量更新员工信息
2
* 将id为 2、4、6、8的员工的薪资在原有基础上增加1000
3
*/
4
@Test
5
public void testUpdateByIds() {
6
Integer[] ids = {2,4,6,8}; //获取所要更新的员工的id数组
7
Double sal = 1000.0; //要涨的薪资
8
//传递的参数超过1个, 将参数封装到map集合中再传递
9
Map<String, Object> map = new HashMap<String, Object>();
10
map.put( "arrIds" , ids );
11
map.put( "sal", sal );
12
int rows = session.update( "EmpMapper.updateByIds", map );
13
System.out.println( "影响行数: "+rows );
14
}
Mapper接口开发
Mapper接口开发介绍
在上面的Mybatis案例中, 通过SqlSession对象调用方法进行增删改查操作时,方法中需要传入的第一个参数是一个字符串值,该值对应的内容为: (Mapper文件中的)namespace + id, 通过这种方式,找到Mapper文件中映射的SQL语句并执行!!
这种方式由于传入的是字符串值, 很容易发生字符串拼写错误且编译时期不会提示。
这里我们将会讲解比上面更加简单的方式,也是我们企业开发中最常用的方式,即使用mapper接口开发。使用mapper接口开发需要注意以下几点:
1
1、创建一个接口,接口的全限定类名和mapper文件的namespace值要相同
2
2、mapper文件中每条要执行的SQL语句,在接口中要添加一个对应的方法,并且接口中的方法名和SQL标签上的id值相同
3
3、Mapper接口中方法接收的参数类型,和mapper.xml中定义的sql的接收的参数类型要相同
4
4、接口中方法的返回值类型和SQL标签上的resultType即返回值类型相同(如果方法返回值是集合,resultType只需要指定集合中的泛型)
Mapper接口开发实现
下面将使用mapper接口开发的方式,实现根据id查询指定的员工信息
1、创建com.tedu.dao.EmpMapper接口
由于接口的全路径名(com.tedu.dao.EmpMapper)要和EmpMapper.xml的namespace值保持一致,因此, 这里将namespace的值改为com.tedu.dao.EmpMapper:
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!DOCTYPE mapper
3
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5
<mapper namespace="com.tedu.dao.EmpMapper">
6
7
</mapper>
2、在接口中提供findById方法
1
/**
2
* 根据id查询员工信息
3
* @param id
4
* @return Emp
5
*/
6
public Emp findById(Integer id);
注意:方法的名字要和映射的sql标签的id值保持一致
方法的返回值类型和resultType的类型要一致
1
<!-- 1.查询:查询Emp表中指定id的员工信息 -->
2
<select id="findById" resultType="com.tedu.pojo.Emp">
3
select * from emp where id=#{id}
4
</select>
3、提供实现类,测试Emp接口中的根据id查询员工的方法
(1)创建com.tedu.test.TestMybatisInf类, 并提供testFindById方法
1
public class TestMybatisInf {
2
@Test
3
public void testFindById() throws Exception{}
4
}
4、实现testFindById方法并测试
1
@Test
2
public void testFindById() throws Exception{
3
......
4
//3.获取Mapper接口对象
5
EmpMapper map = session.getMapper(EmpMapper.class);
6
//4.调用接口对象的方法进行查询
7
Emp e = map.findById(2);
8
//5.输出结果
9
System.out.println(e);
10
}
5、在接口中提供findAll方法
1
/**
2
* 查询所有的员工信息
3
* @return List<Emp>
4
*/
5
public List<Emp> findAll();
注意:方法的名字要和映射的sql标签的id值保持一致
方法的返回值类型和resultType的类型要一致, 例如:
1
<!-- 2.查询Emp表中所有员工的信息 -->
2
<select id="findAll" resultType="com.tedu.pojo.Emp">
3
select * from emp
4
</select>
6、提供实现类,测试Emp接口中的查询所有员工的方法
(1)创建com.tedu.test.TestMybatisInf类, 并提供testFindAll方法
1
public class TestMybatisInf {
2
...
3
@Test
4
public void testFindAll () throws Exception{}
5
}
实现testFindAll方法并测试
1
@Test
2
public void testFindAll() throws Exception{
3
...
4
//3.获取Mapper接口对象
5
EmpMapper map = session.getMapper(EmpMapper.class);
6
//4.调用接口对象的方法进行查询
7
List<Emp> list = map.findAll();
8
//5.输出结果
9
for (Emp e : list) {
10
System.out.println(e);
11
}
12
}
几个可以优化的地方
加入log4j日志框架
在项目中加入log4j的配置文件,用于打印日志信息,便于开发调试。
在src(或相似的目录)下创建log4j.properties如下:
1
# Global logging configuration
2
log4j.rootLogger=DEBUG, stdout
3
# Console output...
4
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
5
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
6
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
mybatis默认使用log4j作为输出日志信息。
只要将该文件放在指定的位置,log4j工具会自动到指定位置加载上述文件,读取文件中的配置信息并使用!
SQL语句中的特殊符号
示例:添加一个查询功能:查询薪资小于3500的所有员工。
1、编辑EmpMapper.xml文件, 添加查询对应的sql.
1
<!-- 练习12: 根据薪资查询员工信息 -->
2
<select id="findBySal" resultType="com.tedu.pojo.Emp">
3
select * from emp
4
where 2=2
5
<if test="minSal != null">
6
and salary >= #{minSal}
7
</if>
8
<if test="maxSal != null">
9
and salary <= #{maxSal}
10
</if>
11
</select>
2、但在书写完后,xml文件提示有错误:
原来,小于号(<)在xml文件中是特殊字符,被xml文件当成了标签的开始符号。
3、解决方法:可以使用 <
代替 <
,例如:
1
<select id="findBySal" resultType="com.tedu.pojo.Emp">
2
select * from emp
3
where 2=2
4
<if test="minSal != null">
5
and salary >= #{minSal}
6
</if>
7
<if test="maxSal != null">
8
and salary <= #{maxSal}
9
</if>
10
</select>
或者是将特殊符号包含在CDATA区( )中,这是因为放在CDATA区中的内容,只会被xml解析器当作普通文本来处理。而不是被当成标签的一部分处理。
1
<!-- 查询薪资小于3500的所有员工 -->
2
<select id="findBySal" resultType="com.tedu.pojo.Emp">
3
select * from emp
4
where 2=2
5
<if test="minSal != null">
6
and salary >= #{minSal}
7
</if>
8
<if test="maxSal != null">
9
and salary <![CDATA[ <= ]]> #{maxSal}
10
</if>
11
</select>
jdbc.properties文件
在开发中,通常我们会将连接数据库的配置信息单独放在一个properties文件中(方便管理和维护), 然后在MyBatis的mapper文件中引入properties文件的配置信息即可!
1、在src目录下创建一个名称为jdbc.properties的文件
2、jdbc.properties文件内容如下:
1
jdbc.driver=com.mysql.jdbc.Driver
2
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
3
jdbc.username=root
4
jdbc.password=root
3、在mybatis-config.xml文件中引入jdbc.properties文件
1、其中 <properties resource="jdbc.properties"/>
标签用于引入jdbc.properties文件,默认到classpath即类目录下寻找指定的文件;
2、properties标签上value属性中配置的 ${http://jdbc.xxx}:
${jdbc.driver}
:其实就是jdbc.properties文件中的 jdbc.driver的值,即:
1
com.mysql.jdbc.Driver
${jdbc.url}
:其实就是jdbc.properties文件中的 jdbc.url的值,即:
1
jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8
${jdbc.username}
:其实就是jdbc.properties文件中的 jdbc.username的值,即:
1
root
${jdbc.password}
:其实就是jdbc.properties文件中的 jdbc.password的值,即:
1
root
扩展内容
Jdbc回顾
通过JDBC查询Emp表中的所有记录,并封装到一个List集合中返回
1、创建TestJdbc类,完成查询所有员工:
1
package com.tedu.jdbc;
2
3
import java.sql.Connection;
4
import java.sql.DriverManager;
5
import java.sql.ResultSet;
6
import java.sql.SQLException;
7
import java.sql.Statement;
8
import java.util.ArrayList;
9
import java.util.List;
10
import com.tedu.pojo.Emp;
11
/** Jdbc回顾 */
12
public class TestJdbc {
13
public static void main(String[] args) {
14
/* 查询emp表中的所有员工信息,将每个员工信息的封装到一个
15
* Emp对象中,再将封装了员工信息所有Emp对象存入List集合
16
* 中,并遍历输出所有的员工信息
17
*/
18
List<Emp> empList = findAll();
19
for(Emp emp : empList){
20
System.out.println(emp);
21
}
22
}
23
24
/**
25
* 查询emp表中的所有员工信息,封装到List集合并返回
26
*/
27
private static List<Emp> findAll() {
28
Connection conn = null;
29
Statement stat = null;
30
ResultSet rs = null;
31
try {
32
//1.注册数据库驱动
33
Class.forName("com.mysql.jdbc.Driver");
34
//2.获取数据库连接(Connection)
35
conn = DriverManager.getConnection(
36
"jdbc:mysql:///yonghedb",
37
"root", "root");
38
//3.获取传输器
39
stat = conn.createStatement();
40
//4.利用传输器发送sql到数据库执行,并返回执行结果
41
String sql = "select * from emp";
42
rs = stat.executeQuery(sql);
43
//5.处理结果
44
//5.1.声明List集合,用于封装所有的员工信息
45
List<Emp> empList = new ArrayList();
46
//5.2.遍历ResultSet结果集
47
while(rs.next()) {
48
//5.3.获取结果集中的每一条员工信息
49
int id = rs.getInt("id");
50
String name = rs.getString("name");
51
String job = rs.getString("job");
52
double salary = rs.getDouble("salary");
53
//5.4.将每一条员工信息封装到一个Emp对象中
54
Emp emp = new Emp();
55
emp.setId(id);
56
emp.setName(name);
57
emp.setJob(job);
58
emp.setSalary(salary);
59
//5.5.将Emp对象存入List集合中
60
empList.add(emp);
61
}
62
return empList;
63
} catch (Exception e) {
64
e.printStackTrace();
65
System.out.println("查询失败!");
66
} finally{
67
//6.释放资源
68
if(rs != null){
69
try {
70
rs.close();
71
} catch (SQLException e) {
72
e.printStackTrace();
73
}finally{
74
rs = null;
75
}
76
}
77
if(stat != null){
78
try {
79
stat.close();
80
} catch (SQLException e) {
81
e.printStackTrace();
82
}finally{
83
stat = null;
84
}
85
}
86
if(conn != null){
87
try {
88
conn.close();
89
} catch (SQLException e) {
90
e.printStackTrace();
91
}finally{
92
conn = null;
93
}
94
}
95
}
96
return null;
97
}
98
}
2、声明Emp实体类,用于封装员工信息:
1
package com.tedu.pojo;
2
/**
3
* 实体类,用于封装Emp表中的一条用户信息
4
*/
5
public class Emp {
6
//1.声明实体类中的属性
7
private Integer id;
8
private String name;
9
private String job;
10
private Double salary;
11
12
//2.提供对应的getter和setter方法
13
public Integer getId() {
14
return id;
15
}
16
public void setId(Integer id) {
17
this.id = id;
18
}
19
public String getName() {
20
return name;
21
}
22
public void setName(String name) {
23
this.name = name;
24
}
25
public String getJob() {
26
return job;
27
}
28
public void setJob(String job) {
29
this.job = job;
30
}
31
public Double getSalary() {
32
return salary;
33
}
34
public void setSalary(Double salary) {
35
this.salary = salary;
36
}
37
38
//3.重写toString方法
39
@Override
40
public String toString() {
41
return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]";
42
}
43
}
在来说说 mybatis架构图
我就截图了 我用的word文档写的,