mybatis学习-day01
0、前言
我觉得还是原生的Mybatis是最好用的!以至于现在不喜欢去使用Mybatis-plus等等工具信息,虽然说方便我们来书写代码,但是对于我们看源码和了解背后原理是毫无意义的。
个人建议:我是不建议上来就去参考官方文档的,首先应该会使用之后。使用熟练之后,然后再去来参考使用官方文档来写代码,这样的使用方式才是最佳的。
一、Mybatis概述
1. 框架简介
什么是框架
- 框架:是整个或部分应用的可重用设计,是可定制化的应用骨架。它可以帮开发人员简化开发过程,提高开发效率。
- 完成一个系统的时候:
- 跟业务需求无关,而又不得不写的代码:==>封装成框架,由框架帮我们实现
- 实现业务需求的功能代码:我们自己在框架基础上进行编写代码
- 简而言之,框架是一个应用系统的半成品,开发人员在框架的基础上,根据业务需求开发功能。即:别人搭台,你唱戏。
框架解决了什么问题
- 框架主要是解决了技术整合问题
一个应用系统,必定要选用大量的技术来完成业务需求,实现功能。这就导致应用系统依赖于具体的技术,一旦技术发生了变化或者出现问题,会对应用系统造成直接的冲击,这是应该避免的。
框架的出现,解决了这个问题:框架是技术的整合。如果使用了框架,在框架基础上进行开发,那么开发人员就可以直接调用框架的API来实现功能,而不需要关注框架的底层具体使用了哪些技术。这就相当于框架“屏蔽”了具体的技术,实现了应用系统功能和技术的解耦。
- 框架一般处于低层应用平台(如JavaEE)和高层业务逻辑之间
有哪些常见的框架
SSM:SpringMVC + Spring + MyBatis
-
SpringMVC:web层,负责和客户端交互
-
Spring:三层解耦,还负责事务管理
-
Mybatis:持久层,负责数据库访问
SSH:Struts + Spring + Hibernate
- Struts1/2:web层,负责和客户端交互
- Spring:三层解耦,还负责事务管理
- Hibernate:持久层,负责数据库访问
2. Mybatis简介
Mybatis介绍
Mybatis是一个优秀的Java轻量级持久层框架。
ibatis:internet abatis,是Apache的框架,后来迁移到了google code,后来代码迁移到了github上
- 它内部封装了JDBC,使开发人员只需要关心SQL语句,而不需要处理繁琐的JDBC步骤
- 它采用了ORM思想,解决了实体和数据库映射的问题。只要提供好sql语句,配置了映射,Mybatis会自动根据参数值动态生成SQL,执行SQL并把结果封装返回给我们。
- 它支持XML和注解两种方式配置映射。
ORM思想
ORM:Object Relational Mapping,对象关系映射思想。指把Java对象和数据库的表和字段进行关联映射,从而达到操作Java对象,就相当于操作了数据库。查询了数据库,自动封装成JavaBean对象
Mybatis是一个半ORM框架:
-
在查询时,Mybatis会自动把查询的数据,帮我们封装成Java对象:关系==>对象 OK
-
在增删改时,Mybatis不能帮我们把JavaBean的数据,自动映射到表字段里:对象==>关系 不行
3. Mybatis下载
- Mybatis官网:http://www.mybatis.org/mybatis-3/zh/index.html
- Mybatis下载地址:https://github.com/mybatis/mybatis-3/releases/download/mybatis-3.4.6/mybatis-3.4.6.zip
- Mybatis源码下载:https://github.com/mybatis/mybatis-3/archive/mybatis-3.4.6.zip
二、快速入门
需求描述
查询所有用户信息,获取用户集合List<User>
准备工作
1、实现步骤
-
创建Java项目,导入依赖,准备JavaBean
-
编写Mybatis的代码,查询所有用户。
-
编写测试代码
功能实现
1 创建Maven项目,准备JavaBean
1) 创建java项目,导入依赖
<dependencies>
<!--Junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--MySql的数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--Mybatis的jar包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--Mybatis依赖的日志包-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2) 创建JavaBean
- 在
com.guang.pojo
里创建User
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//get/set...
//toString...
}
2 编写Mybatis的代码,查询所有用户
1) 创建dao接口(映射器)
dao层的接口叫映射器,映射器的类名,可以叫XXXMapper
,也可以叫XXXDao
。我们这里按照之前的习惯,取名UserDao
注意:只要创建接口即可,不需要创建接口的实现类。因为这里最终得到的是代理对象,JDK动态生成的。
public interface UserDao {
List<User> queryAll();
}
2) 准备映射文件xml
在resources目录下创建文件的时候,一定要注意路径:
com/guang/UserDao.xml
-
映射文件名称要和映射器类名一样。例如:映射器叫UserDao,那么配置文件就叫UserDao.xml
-
映射文件位置要和映射器位置一样。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:给哪个接口配置的映射,写接口的全限定类名-->
<mapper namespace="com.guang.dao.UserDao">
<!--select标签:表示要执行查询语句; id:给接口里哪个方法配置的,写方法名;resultType:结果集封装类型-->
<select id="queryAll" resultType="com.guang.pojo.User">
select * from user
</select>
</mapper>
3) 准备Mybatis的日志配置文件
Mybatis支持使用log4j输出执行日志信息,但是需要我们提供log4j的配置文件:log4j.properties
注意:
- 如果没有log4j.properties,不影响Mybatis的功能,只是没有详细日志而已
- 如果需要日志的话,要把log4j.properties文件放到src目录下。log4j.properties内容如下:
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
4) 准备Mybatis的全局配置文件xml
- 全局配置文件的名称随意,我们习惯叫 SqlMapConfig.xml
- 全局配置文件的位置随意,我们习惯放到src的根目录下
<?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>
<!--配置默认的数据库环境-->
<environments default="mysql_mybatis">
<!--定义一个数据库连接环境-->
<environment id="mysql_mybatis">
<!--设置事务管理方式,固定值JDBC-->
<transactionManager type="JDBC"/>
<!--设置数据源,POOLED,UNPOOLED,JNDI,我们使用POOLED-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- !!!配置映射文件的位置!!!!!!!!!!!!!!这一步千万不要忘记!!!!!!!!!!!!!! -->
<mappers>
<mapper resource="com/guang/dao/UserDao.xml"/>
</mappers>
</configuration>
3 编写测试代码
public class MybatisTest {
@Test
public void testQuickStart() throws IOException {
//1. 读取全局配置文件SqlMapConfig.xml
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
//2. 创建SqlSessionFactoryBuilder构造者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3. 使用构造者builder,根据配置文件的信息is,构造一个SqlSessionFactory工厂对象
SqlSessionFactory factory = builder.build(is);
//4. 使用工厂对象factory,生产一个SqlSession对象
SqlSession session = factory.openSession();
//5. 使用SqlSession对象,获取映射器UserDao接口的代理对象
UserDao dao = session.getMapper(UserDao.class);
//6. 调用UserDao代理对象的方法,查询所有用户
List<User> users = dao.queryAll();
for (User user : users) {
System.out.println(user);
}
//7. 释放资源
session.close();
is.close();
}
}
小结
- 在dao包里创建映射器,在接口里写方法
- 映射器:dao层的接口。不要实现类
- 在resource里创建映射文件
- 映射文件名称 必须和映射器名称相同
- 映射文件位置 必须和映射器位置相同
- 给每个方法配置sql语句
- 在resources里创建全局配置文件
- 名称随意,位置建议放到resources里
- 里边配置数据库环境,所有的映射文件路径
- 准备log4j.properties
MyBatis涉及的设计模式:
* 构造者模式:用于隐藏复杂的构造过程,帮我们构造一个对象出来
* 工厂模式:由工厂帮我们生产一些对象,目的是解耦
* 代理模式:在不能直接调用目标对象,或者不方便直接调用时,使用代理模式生成的代理对象来间接调用
三、代理方式CURD
需求说明
针对user表进行CURD操作:
- 查询全部用户,得到
List<User>
- 保存用户(新增用户)
- 修改用户
- 删除用户
- 根据主键查询一个用户,得到
User
- 模糊查询
- 查询数量
准备工作
在单元测试类中准备好@Before
、@After
的方法代码备用。编写完成一个功能,就增加一个@Test方法即可。
代码如下:
public class MybatisCURDTest {
private InputStream is;
private SqlSession session;
private UserDao dao;
@Before
public void init() throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
session = factory.openSession();
dao = session.getMapper(UserDao.class);
}
@After
public void destory() throws IOException {
//释放资源
session.close();
is.close();
}
}
新增用户
1) 在映射器中增加方法
- 在
UserDao
中增加方法
void save(User user);
2) 在映射文件中添加配置
- 在
UserDao.xml
中增加配置- SQL语句中
#{}
代表占位符,相当于预编译对象的SQL中的?
,具体的值由User类中的属性确定
- SQL语句中
<!--parameterType:是方法的参数类型,写全限定类名-->
<insert id="save" parameterType="com.guang.pojo.User">
<!--
selectKey标签:用于向数据库添加数据之后,获取最新的主键值
resultType属性:得到的最新主键值的类型
keyProperty属性:得到的最新主键值,保存到JavaBean的哪个属性上
order属性:值BEFORE|AFTER,在insert语句执行之前还是之后,查询最新主键值。MySql是AFTER
-->
<selectKey resultType="int" keyProperty="id" order="AFTER">
select last_insert_id()
</selectKey>
insert into user (id, username, birthday, address, sex)
values (#{id}, #{username}, #{birthday},#{address},#{sex})
</insert>
3) 编写测试代码
- 在单元测试类里增加测试方法
- 注意:执行了DML语句之后,一定要提交事务:session.commit();
那么这里来说一下,在哪里将自动提交设置成了false呢?
看一下关键步骤:
SqlSession session = factory.openSession();
然后跟进去,来到org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
可以看到将最后一个参数,也就是autoCommit设置成了false,还可以一直跟到事务的地方,可以看到是将事务那里也设置了关闭自动提交
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("tom");
user.setAddress("广东深圳");
user.setBirthday(new Date());
user.setSex("男");
System.out.println("保存之前:" + user);//保存之前,User的id为空
dao.save(user);
session.commit();//注意:必须要手动提交事务
System.out.println("保存之后:" + user);//保存之后,User的id有值
}
修改用户
1) 在映射器中增加方法
- 在
UserDao
中增加方法
void edit(User user);
2) 在映射文件中添加配置
- 在
UserDao.xml
中增加配置
<update id="edit" parameterType="com.guang.pojo.User">
update user set username = #{username}, birthday = #{birthday},
address = #{address}, sex = #{sex} where id = #{id}
</update>
3) 编写测试代码
- 注意:执行了DML语句之后,一定要提交事务:session.commit();
@Test
public void testEditUser(){
User user = new User();
user.setId(50);
user.setUsername("jerry");
user.setAddress("广东深圳宝安");
user.setSex("女");
user.setBirthday(new Date());
dao.edit(user);
session.commit();
}
删除用户
1) 在映射器中增加方法
- 在
UserDao
中增加方法
void delete(Integer id);
2) 在映射文件中增加配置
-
在
UserDao.xml
中增加配置 -
注意:
- 如果只有一个参数,且参数是简单类型的,那么#{id}中的id可以随意写成其它内容,例如:
#{abc}
- 简单类型参数:8种基本数据类型及其包装类, 和字符串
- 如果只有一个参数,且参数是简单类型的,那么#{id}中的id可以随意写成其它内容,例如:
<delete id="delete" parameterType="int">
delete from user where id = #{id}
</delete>
3) 编写测试代码
-
在单元测试类里增加方法
-
执行了DML语句之后,一定要提交事务:session.commit();
@Test
public void testDeleteUser(){
dao.delete(50);
session.commit();
}
查询功能
根据主键查询一个用户
1) 在映射器中增加方法
- 在
UserDao
中增加方法
User findById(Integer id);
2) 在映射文件中增加配置
- 在
UserDao.xml
中增加配置
<select id="findById" parameterType="int" resultType="com.guang.pojo.User">
select * from user where id = #{id}
</select>
3) 编写测试代码
- 在单元测试类里增加方法
@Test
public void testFindUserById(){
User user = dao.findById(48);
System.out.println(user);
}
模糊查询-#{}
方式
1 在映射器中增加方法
/**
* 使用#{}方式进行模糊查询
*/
List<User> findByUsername1(String username);
2 在映射文件中增加配置
- 注意:只有一个参数,且是简单参数时,
#{}
中可以写成其它任意名称,例如:#{abc}
<select id="findByUsername1" parameterType="string" resultType="com.guang.pojo.User">
select * from user where username like #{username}
</select>
3 编写测试代码
- 注意:模糊查询的条件值,前后需要有%
/**
* 使用#{}方式进行模糊查询--单元测试方法
*/
@Test
public void testFindUserByUsername1(){
List<User> users = dao.findByUsername1("%王%");
for (User user : users) {
System.out.println(user);
}
}
模糊查询-${}
方式
1 在映射器中增加方法
/**
* 使用${value}方式进行模糊查询
*/
List<User> findByUsername2(String username);
2 在映射文件中增加配置
- Mybatis3.4版本中
${value}
是固定写法,不能做任何更改(高版本Mybatis#{}
中可以随意写) - 在SQL语句中已经加了%,那么在测试代码中,传递参数值时就不需要再加%了
<select id="findByUsername2" parameterType="string" resultType="com.guang.pojo.User">
select * from user where username like '%${value}%'
</select>
3 编写测试代码
/**
* 使用${value}方式进行模糊查询--单元测试方法
*/
@Test
public void testFindUserByUsername2(){
//注意:SQL语句中已经加了%, 参数值前后不需要再加%
List<User> users = dao.findByUsername2("王");
for (User user : users) {
System.out.println(user);
}
}
#{}
和${}
的区别【面试题】
-
#{}
:表示一个占位符,相当于预编译对象的SQL中的?- 可以有效防止SQL注入;
- Mybatis会自动进行参数的Java类型和JDBC类型转换;
#{}
写的是属性名称。如果只有一个参数并且是简单类型,里边可以是value或者其它名称
映射文件中的SQL:select * from user where username like #{username} 单元测试中传递实参:%王% 最终执行的SQL:select * from user where username like ? 参数值:%王%
-
${}
:表示拼接SQL串,相当于把实际参数值,直接替换掉${}
-
不能防止SQL注入
-
Mybatis不进行参数的Java类型和JDBC类型转换
-
${}
写的是属性名称。如果只有一个参数并且是简单类型,${value}
中只能是value,不能是其它名称(Mybatis3.4 里存在这种现象,3.5没也可以随意写了)
映射文件中的SQL:select * from user where username like '%${value}%' 单元测试中传递实参:王 最终执行的SQL:select * from user where username like '%王%'
-
查询数量
1) 在映射器UserDao中增加方法
Integer findTotalCount();
2) 在映射文件中UserDao.xml增加配置
<select id="findTotalCount" resultType="int">
select count(*) from user
</select>
3) 在单元测试类中编写测试代码
@Test
public void testFindTotalCount(){
Integer totalCount = dao.findTotalCount();
System.out.println(totalCount);
}
功能小结
-
使用Mybatis开发的步骤:
- 在映射器里增加方法
- 在映射文件里增加配置
-
在映射文件里:
- 有四种标签,用于配置不同的语句:
select
:查询标签。- id:对应的方法名称
- parameterType:参数类型
- resultType:查询结果集封装的类型
insert
:插入- id:对应的方法名称
- parameterType:参数类型
update
:修改- id:对应的方法名称
- parameterType:参数类型
delete
:删除- id:对应的方法名称
- parameterType:参数类型
- 四种标签里边写的是sql语句
- 使用
#{}
获取参数值,使用${}
获取参数值 - 两者的区别是:
#{}
:本质是占位符,使用预编译的方式- 可以防止 sql注入漏洞
- 可以自动进行Java类型和JDBC类型的转换
- 如果只有一个参数,并且是简单类型,
#{这里可以随意写}
${}
:本质是字符串的拼接,没有使用预编译- 不能防止sql注入
- 不能自动进行Java类型和JDBC类型的转换
- 如果只有一个参数,并且是简单类型,
${value}
- 使用
- 有四种标签,用于配置不同的语句:
四、参数和结果集
1. OGNL简介
-
OGNL:Object Graphic Navigator Language,是一种表达式语言
-
OGNL参考官网地址http://commons.apache.org/proper/commons-ognl/language-guide.html
-
在
#{}
里、${}
可以使用OGNL表达式,可以:- 从JavaBean中获取指定属性的值,本质上使用的是JavaBean的getXxx()方法。
- 进行数据运算
- 调用对象的方法和属性
- ... ...
-
例如:
-
user.getUsername()
--->user.username
-
user.getAddress().getProvince()
--->user.address.province
-
list.size()>0
,list != null
,array.length > 0
,str.length() > 0
-
2. parameterType
parameterType用来配置方法的参数类型,通常可以不写。
不同的参数类型和个数,会影响sql语句中获取参数值的方式。
方法有一个参数
参数是简单类型
- 简单参数:指8种基本数据类型及其包装类, 或者字符串
- 参数写法:
- 包装类全限定类名:
java.lang.Integer
,java.lang.Double
等等 - 基本数据类型名称:
int
,double
,short
等基本数据类型 - 字符串:
string
, 或java.lang.String
- 包装类全限定类名:
- SQL语句里获取参数:如果是一个简单类型参数,写法是:
#{随意}
List<User> findByUsername(String username);
<select id="findByUsername" resultType="com.guang.pojo.User">
<!-- 只有一个参数,并且是简单类型,那么 #{}里可以随意写 -->
select * from user where username like #{username}
</select>
参数是POJO(JavaBean)
-
参数写法:要写全限定类名
- parameterType写全限定类名
com.guang.pojo.User
- parameterType写全限定类名
-
SQL语句里获取参数:
#{JavaBean的属性名}
List<User> findByUser(User user);
<select id="findByUser" parameterType="com.guang.pojo.User" resultType="com.guang.pojo.User">
<!-- 只有一个参数,参数是JavaBean,那么 #{}里写JavaBean的属性名 -->
select * from user where username like #{username} and sex = #{sex}
</select>
参数是POJO包装类(复杂JavaBean)
注意:这个也是很有用的一个点。经常在前端的搜索栏中需要使用到这个点。
- 参数写法:
在web应用开发中,通常有综合条件的搜索功能,例如:根据商品名称 和 所属分类 同时进行搜索。这时候通常是把搜索条件封装成JavaBean对象;JavaBean中可能还有JavaBean。
-
SQL里取参数:
#{xxx.xx.xx}
-
例如:根据用户名搜索用户信息,查询条件放到QueryVO的user属性中。QueryVO如下:
public class QueryVO {
private User user;
//private String className;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
1) 在映射器UserDao中增加方法
List<User> findByVO(QueryVO vo);
2) 在映射文件中增加配置信息
<select id="findByVO" parameterType="com.guang.pojo.QueryVO" resultType="com.guang.pojo.User">
select * from user where username like #{user.username}
</select>
3) 在单元测试类中编写测试代码
@Test
public void testFindByVO(){
QueryVO vo = new QueryVO();
User user = new User();
user.setUsername("%王%");
vo.setUser(user);
List<User> users = dao.findByVO(vo);
for (User user1 : users) {
System.out.println(user1);
}
}
参数是Map
List<User> findByMap(Map<String,Object> map);
<select id="findByMap" parameterType="java.util.Map" resultType="User">
<!-- 参数是Map,那么 #{}里写Map的key -->
select * from user where user_name like #{username} and sex = #{sex}
</select>
@Test
public void testFindByMap(){
Map<String, Object> map = new HashMap<>();
map.put("username", "%王%");
map.put("sex", "男");
List<User> userList = userDao.findByMap(map);
for (User user : userList) {
System.out.println(user);
}
}
参数是List
List<User> findByList(List<Integer> ids);
<select id="findByList" resultType="User">
<!-- 参数是List,那么#{}里写 list[索引],或者 collection[索引] -->
select * from user where id in(#{list[0]}, #{list[1]})
</select>
@Test
public void testFindByList(){
List<Integer> ids = new ArrayList<>();
ids.add(41);
ids.add(42);
List<User> userList = userDao.findByList(ids);
for (User user : userList) {
System.out.println(user);
}
}
方法有多个参数
默认取值方式
List<User> findByNameAndSex(String name, String sex);
<select id="findByNameAndSex" resultType="User">
<!--
如果有多个参数,那么默认情况下 #{}里可以写:
arg0表示第一个参数, arg1表示第二个参数, 以此类推
或者
param1表示第一个参数,param2表示第二个参数,以此类推
-->
select * from user where user_name like #{arg0} and sex = #{arg1}
</select>
命名参数方式
List<User> findByNameAndSex2(@Param("username") String name,
@Param("sex") String sex);
<select id="findByNameAndSex2" resultType="User">
<!--
如果多个参数:
如果方法参数上有@Param注解,那么可以写:#{参数名}或 #{param1}
或者
如果方法参数上没有@Param注解,那么可以写:#{arg0}或#{param1}表示第一个参数,以此类推
-->
select * from user where user_name like #{username} and sex = #{sex}
</select>
源码分析【拓展】
如果有多个参数,Mybatis会把参数封装成Map
- 以实参参数名为key,以参数值为value,放到Map里
- 以param+序号为key,以参数值为value,放到Map里
所以在sql里获取参数时才可以写:#{arg0}
, #{arg1}
, #{param1}
, #{param2}
ParamNameResolver的构造方法
private static final String GENERIC_NAME_PREFIX = "param";
/**
* key是索引,value是参数名称
* 参数名称:
* 如果配置了@Param,就是@Param配置的名称
* 如果没有@Param,就以参数索引为名称
*/
private final SortedMap<Integer, String> names;
public ParamNameResolver(Configuration config, Method method) {
final Class<?>[] paramTypes = method.getParameterTypes();
//获取每个方法参数上的注解。一个方法有多个参数,一个参数可能有多个注解,所以以二维数组接收
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
int paramCount = paramAnnotations.length;
// 循环处理方法上的每个参数
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
//循环处理参数上的每个注解。如果有@Param注解,就以@Param指定的值为参数名称
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
//如果没有获取到@Param配置的名称,就获取方法的参数名称
//按照jdk规范,反射获取的方法参数名称 默认是arg0、arg1...
if (name == null) {
// @Param was not specified.
if (config.isUseActualParamName()) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
ParamNameResolver的getNamedParams()方法
//args:是调用映射器方法时,传递进去的参数值
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
//如果没有参数值,返回null
if (args == null || paramCount == 0) {
return null;
//如果只有一个参数,并且没有@Param注解,把参数值直接返回,未命名
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
//如果有多个参数,或者参数上有@Param注解
} else {
//创建一个Map,把所有参数放到Map里,返回这个Map
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//以构造方法里解析得到的参数名为key,以参数值为value,放到map里
param.put(entry.getValue(), args[entry.getKey()]);
//以 param+序号 为key,以参数值为value,放到Map里
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
DefaultSqlSession的wrapCollection()方法
private Object wrapCollection(final Object object) {
if (object instanceof Collection) {
//如果参数值是Collection类型的,以collection为key,以参数值为value,放到map里
StrictMap<Object> map = new StrictMap<Object>();
map.put("collection", object);
if (object instanceof List) {
//如果参数值是List类型的,再以list为key,以参数值为value,放到map里
map.put("list", object);
}
return map;
} else if (object != null && object.getClass().isArray()) {
//如果参数值是数组,就以array为key,以参数值为value,放到map里
StrictMap<Object> map = new StrictMap<Object>();
map.put("array", object);
return map;
}
return object;
}
小结
- 如果只有一个参数:
- 参数是简单类型,
#{}
随意写 - 参数是POJO,
#{POJO的属性名}
- 参数是Map,
#{map的key}
- 参数是List,
#{list[索引]}
或者#{collection[索引]}
- 参数是简单类型,
- 如果有多个参数:
- 默认情况下:
#{arg0}, #{arg1}, ...
获取参数。仅适用于参数上没有@Param注解的情况#{param1},#{param2},...
获取参数
- 使用@Param注解声明参数名称
#{参数名称}
#{param1},#{param2},...
获取参数
- 默认情况下:
3.规范
在阅读了上面的源码之后,我觉得这里应该来建立起来一个规范。
不管方法参上有多少个注解,是否是基础类型,那么在使用的时候都应该来建立对应的开发规范。因为ONGL表达式可以来做对应的操作。
所以Dao接口中使用的时候,都需要在参数位置上添加上@Param注解,来指定参数的名字;在xml文件中进行书写的时候直接利用ONGL表达式来进行书写即可。我可能在这个地方有点强迫症,我只看对应的接口方法中的@Param注解来进行开发。
那么来演示一下上面的操作:
方法上只有一个参数
参数是简单类型
List<User> findByUsername(@Param("username") String username);
对应的xml文件中书写:
<select id="findByUsername" parameterType="string" resultType="com.guang.pojo.User">
select * from user where username = #{username}
</select>
参数是POJO包装类(复杂JavaBean)
接口方法:
/**
* 根据用户的姓名和性别来查询对应的用户集合
* @param user
* @return
*/
List<User> findByUser(@Param("user") User user);
对应的xml文件
<select id="findByUser" parameterType="com.guang.pojo.User" resultType="com.guang.pojo.User">
select * from user where username = #{user.username} and sex = #{user.sex}
</select>
参数是POJO包装类(复杂JavaBean)
对应的实体类对象:
public class QueryVO {
private User user;
// getter/setter方法
}
对应的接口
List<User> findByVO(@Param("queryVo") QueryVO vo);
对应的xml
<select id="findByVO" parameterType="com.guang.pojo.QueryVO" resultType="com.guang.pojo.User">
select * from user where username = #{queryVo.user.username}
</select>
参数是Map
当参数是map的时候应该注意了,因为这里在xml中需要将key作为对应的主键,所以应该先写key,然后再向xml文件中来写对应的值。
接口
/**
* 根据map中的key来构成条件来进行条件。所以这里需要将key确定下来,所以这里一般说来,也很少会有人来使用这种方式来进行操作
* @param map 封装条件的map
* @return
*/
List<User> findByMap(@Param("map")Map<String,Object> map);
xml文件
<select id="findByMap" parameterType="map" resultType="com.guang.pojo.User">
select * from user where username like #{map.username} and sex=#{map.sex}
</select>
对应的测试:
/**
* 当map中的条件回写完成之后,应该立刻马上去写xml文件中的key
*/
@Test
public void testFindByMap(){
// 利用map来构建条件
Map<String, Object> map = new HashMap<>();
map.put("username", "%王%");
map.put("sex", "男");
List<User> userList = dao.findByMap(map);
for (User user : userList) {
System.out.println(user);
}
}
参数是List或者是array
下面的foreach标签会在下一篇章来进行介绍。
是list或者是array套路都是一样的。唯一不同的一点就是参数位置,一个是list,一个是数组而已
List<User> findByList(@Param("ids") List<Integer> ids);
对应的xml文件
<select id="findByList" parameterType="list" resultType="com.guang.pojo.User">
select * from user where id in
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</select>
测试:
@Test
public void testfindByList(){
List<Integer> integerList = Arrays.asList(41, 42, 43);
List<User> userList = dao.findByList(integerList);
userList.forEach(System.out::println);
}
可以看到控制台输出的SQL语句即可。
那么看下数组的方式
接口:
List<User> findByArray(@Param("ids")Integer[] ids);
对应的xml文件
<select id="findByArray" parameterType="object" resultType="com.guang.pojo.User">
select * from user where id in
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</select>
测试类:
@Test
public void testfindByArray(){
Integer[] integers = new Integer[3];
integers[0]=41;
integers[1]=42;
integers[2]=43;
List<User> userList = dao.findByArray(integers);
userList.forEach(System.out::println);
}
一般来说,这种查询都很少来进行使用。尤其是数组类型的,几乎都不会来这么使用的。
4. 结果集映射
如果执行的是查询,那么Mybatis会帮我们把查询的结果集自动映射成指定的类型。我们可以在select
标签上使用resultType
属性进行自动映射, 或使用resultMap
属性来手动设置映射关系。
-
resultType
:设置查询结果集的封装类型,Mybatis会把查询结果自动映射到指定的类型上。
但是要注意:如果
resultType
设置为JavaBean,要求JavaBean的属性名和字段名保持一致,否则不能封装 -
resultMap
:用于手动设置字段和JavaBean的映射关系,它非常灵活也非常强大 【重点】
resultType自动映射
resultType是简单类型
简单类型指:8种基本数据类型及其包装类,和字符串
<select id="xxx" resultType="int"></select>
<select id="xxx" resultType="java.lang.Integer"></select>
<select id="xxx" resultType="java.lang.String"></select>
resultType是POJO类型
POJO指的是Plain Ordinary Java Object,简单Java对象,实际就是普通JavaBeans。
当resultType
的值是JavaBean时,要求JavaBean的属性名要和字段名保持一致,否则Mybatis不能自动映射
<select id="xxx" resultType="com.guang.pojo.User"></select>
驼峰式命名映射
数据库里字段命名,通常是是A_COLUMN
风格的,而JavaBean的属性命名通常是驼峰式命名aColumn
风格的。这种情况下,字段名和属性名并不一致,但是Mybatis仍然支持自动映射,而我们只要开启一个全局开关即可。
开启驼峰式命名映射
- 修改全局配置文件,开启下划线和驼峰式命名映射的开关
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
属性名和字段名不一致
有User2
如下,属性名和数据库表的字段名不同。要求查询user表的所有数据,封装成User2的集合。
public class User2 implements Serializable{
private Integer userId;
private String username;
// jdk8中支持将LocalDate转换成date类型
private Date userBirthday;
private String userSex;
private String userAddress;
//get/set...
//toString...
}
resultType
是POJO类型时,要求POJO属性名和字段名必须一致,Mybatis才能自动映射成功。那么如果不一致会出现什么情况,又该如何处理呢?
-
方案一:SQL语句中给查询的列起别名,别名和JavaBean属性名保持一致
-
方案二:使用
resultMap
代替掉resultType
,手动设置映射关系 【这种方式比较习惯来进行使用!】
这里我们先演示方案一:
1) 在映射器UserDao中增加方法
/**
* JavaBean属性名和字段名不一致的情况处理---方案一
* @return
*/
List<User2> queryAll_plan1();
2) 在映射文件UserDao.xml中增加statement
<select id="queryAll_plan1" resultType="com.guang.pojo.User2">
select id as userId, username as username, birthday as userBirthday, address as userAddress, sex as userSex from user
</select>
3) 在单元测试类中编写测试代码
/**
* JavaBean属性名和字段名不一致的情况处理---方案一 单元测试代码
*/
@Test
public void testQueryAllUser2_plan1(){
List<User2> user2List = dao.queryAll_plan1();
for (User2 user2 : user2List) {
System.out.println(user2);
}
}
resultMap手动映射
resultMap
和resultType
是不能同时存在于select
标签上的,只能二选一。
我们这里使用resultMap来解决 JavaBean属性名和字段名不一致的情况
1) 在映射器中增加方法
/**
* JavaBean属性名和字段名不一致的情况处理--方案二
* @return
*/
List<User2> queryAll_plan2();
2) 在映射文件中增加配置
<select id="queryAll_plan2" resultMap="user2Map">
select * from user
</select>
<!--
resultMap标签:设置结果集中字段名和JavaBean属性的对应关系
id属性:唯一标识
type属性:要把查询结果的数据封装成什么对象,写全限定类名
-->
<resultMap id="user2Map" type="com.guang.pojo.User2">
<!--id标签:主键字段配置。 property:JavaBean的属性名; column:字段名-->
<id property="userId" column="id"/>
<!--result标签:非主键字段配置。 property:JavaBean的属性名; column:字段名-->
<result property="username" column="username"/>
<result property="userBirthday" column="birthday"/>
<result property="userAddress" column="address"/>
<result property="userSex" column="sex"/>
</resultMap>
3) 编写测试代码
/**
* JavaBean属性名和字段名不情况处理--方案二 单元测试代码
*/
@Test
public void testQueryAllUser2_plan2(){
List<User2> user2List = dao.queryAll_plan2();
for (User2 user2 : user2List) {
System.out.println(user2);
}
}
小结
- 映射器里加方法
- 映射文件里加配置:使用resultMap代替掉resultType,两者不能共存
<select id="xxx" resultMap="resultMap的id">
sql语句
</select>
<resultMap id="resultMap的id" type="映射的JavaBean的全限定类名">
<!-- id配置主键字段映射(建议一定要写上);result配置非主键字段映射 -->
<id property="" column=""/>
<result property="" column=""/>
</resultMap>
五、全局配置文件
SqlMapConfig.xml中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名) ★
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器) ★
1. typeAlias类型别名
在映射文件中,我们要写大量的parameterType和resultType,如果全部都写全限定类名的话,代码就太过冗余,开发不方便。可以使用类型别名来解决这个问题。
类型别名:是Mybatis为Java类型设置的一个短名称,目的仅仅是为了减少冗余。
注意:类型别名不区分大小写
Mybatis内置别名
Mybatis提供的别名有:
别名 | 映射的类型(Java类型) |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | java.lang.String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
自定义类型别名
自己定义的JavaBean,全限定类名太长,可以自定义类型别名。
给一个类指定别名
1) 配置一个类的别名
- 在全局配置文件中,增加如下配置
<typeAliases>
<!-- type:要指定别名的全限定类名 alias:别名 -->
<typeAlias type="com.guang.pojo.QueryVO" alias="vo"/>
<typeAlias type="com.guang.pojo.User" alias="user"/>
<typeAlias type="com.guang.pojo.User2" alias="user2"/>
</typeAliases>
2) 在映射文件中使用类型别名
<!-- parameterType使用别名:vo, resultType使用别名:user -->
<select id="findByVO" parameterType="vo" resultType="user">
select * from user where username like #{user.username}
</select>
批量注册别名
我们可以使用package
标签,指定一个包,Mybatis会把包里的类自动注册别名,别名即类名。
1) 为一个package下所有类注册别名
- 在全局配置文件中增加如下内容:
<typeAliases>
<!-- 把com.guang.pojo包下所有JavaBean都注册别名,类名即别名,不区分大小写 -->
<package name="com.guang.pojo"/>
</typeAliases>
2) 在映射文件中使用类型别名
<!-- parameterType使用别名:queryvo, resultType使用别名:user -->
<select id="findByVO" parameterType="queryvo" resultType="user">
select * from user where username like #{user.username}
</select>
别名冲突的处理
如果使用了批量注册别名,那么可能多个包里的相同名称的类,例如有com.guang.pojo.User
,com.guang.pojo.User
,这个时候两个类的别名都是user
,就会造成冲突。
如何处理别名冲突呢?在Java类上使用注解@Alias
指定别名即可:
package com.guang.pojo;
@Alias("user1")
public class User{}
package com.guang.pojo;
@Alias("user2")
public class User{}
2. mappers映射器
用来配置映射器接口的配置文件位置,或者映射器接口的全限定类名
2.1 <mapper resource=""/>
用于指定映射文件xml的路径,支持xml开发方式,例如:
<mappers>
<mapper resource="com/guang/dao/UserDao.xml"/>
</mappers>
注意:
映射文件的名称,和映射器接口类名 可以不同
映射文件的位置,和映射器接口位置 可以不同
配置了xml的路径,Mybatis就可以加载statement信息,并且根据namespace属性找到映射器
2.2 <mapper class=""/>
用于指定映射器接口的全限定类名,支持XML开发和注解开发,例如:
<mappers>
<mapper class="com.guang.dao.UserDao"/>
</mappers>
如果是使用xml方式开发,那么要注意:
映射文件的名称 要和 映射器接口的类名相同
映射文件的位置 要和 映射器接口的位置相同
Mybatis只知道映射器的名称和位置,不知道配置文件的名称和位置。只能查找同名同路径的配置文件
2.3 <package name=""/>
用于自动注册指定包下所有的映射器接口,支持XML开发和注解开发,例如:
<mappers>
<package name="com.guang.dao"/>
</mappers>
如果是使用XML方式开发,那么要注意:
映射文件的名称 要和 映射器接口的类名相同
映射文件的位置 要和 映射器接口的位置相同
Mybatis只能根据包名找到所有的映射器的类名和位置, 不知道配置文件的名称和位置。只能查找同名同路径的配置文件
小结
- 在全局配置文件里,加上映射器配置:
<mappers>
<package name="com.guang.dao"/>
</mappers>
- 注意:
- 映射器接口名称、位置,必须和映射文件的名称、位置 相同
六、数据源和事务【了解】
1. Mybatis的数据源
全局配置文件,会被Mybatis封装成一个Configuration
对象
Mybatis中的数据源,是指全局配置文件中<dataSouce></dataSouce>
的配置。Mybatis为了提高数据库操作的性能,也使用了连接池的技术,但是它采用的是自己开发的连接池技术。
1.1 Mybatis中dataSouce的分类
1.1.1 三种dataSouce介绍
-
UNPOOLED:不使用连接池技术的数据源
对应Mybatis的
UnpooledDataSouce
类,虽然也实现了javax.sql.DataSource
接口,但是它的getConnection()
方法中,并没有真正的使用连接池技术,而是直接从数据库中创建的连接对象,即:DriverManager.getConnection()
方法 -
POOLED:使用连接池技术的数据源
对应Mybatis的
PooledDataSouce
类,它实现了javax.sql.DataSouce
接口,同时采用了Mybatis自己开发的连接池技术,是我们使用最多的一种数据源 -
JNDI:使用JNDI技术的数据源
采用服务器软件提供的JNDI技术实现,从服务器软件中获取数据源。从不同服务器软件中得到的
DataSouce
对象不同。例如:Tomcat中配置了数据连接信息,我们的web应用部署到Tomcat里,就可以获取Tomcat中配置的数据源,而不需要web应用自己再配置数据库连接信息。
1.1.2 三种dataSouce的关系与源码分析
UnpooledDataSource
和PooledDataSource
都实现了javax.sql.DataSource
接口UnpooledDataSource
没有使用连接池技术,它的getConnection()
方法是从数据库中创建的连接PooledDataSource
采用了连接池技术- 它内部有
UnpooledDataSource
的引用,当需要创建新连接时,是调用UnpooledDataSource
来获取的 - 它只是提供了用于存放连接对象的池子
- 它内部有
1.1.3 PooledDataSource
获取新连接的过程源码分析
1.2 Mybatis中dataSouce的配置
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis49"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
当Mybatis读取全局配置文件时,会根据我们配置的dataSource标签的type,来生成对应的dataSource对象
2. Mybatis的事务
因为Mybatis的是对JDBC的封装,所以Mybatis在本质上也是基于Connection
对象实现的事务管理,只是把管理的代码封装起来了,是使用SqlSession
对象进行事务管理的。
默认事务管理方式
默认情况下,我们使用工厂对象的openSession()
方法得到的SqlSession
对象,是关闭了事务自动提交的,即:默认情况下,SqlSession
是开启了事务的,需要手动提交。
获取session对象:factory.openSession()
操作完数据库之后,需要手动提交事务:sqlSession.commit();
如果要回滚事务,就使用方法:sqlSession.rollback();
自动提交事务实现
Mybatis也支持自动提交事务,操作方法如下:
- 获取SqlSession对象:
factory.openSession(true)
- 操作数据库,事务会自动提交
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//获取的是自动提交事务的SqlSession,所以不需要再手动关闭事务
SqlSession session = factory.openSession(true);
UserDao dao = session.getMapper(UserDao.class);
//操作数据库
dao.delete(48);
//释放资源,事务会自动提交,所以不需要再手动提交事务
session.close();
is.close();
将来Mybatis和Spring整合后,会由Spring来进行事务管理。
所以Mybatis的事务管理,了解即可
七、实现类方式CURD【拓展了解】
Mybatis中提供了两种dao层的开发方式:一是使用映射器接口代理对象的方式;二是使用映射器接口实现类的方式。其中代理对象的方式是主流,也是我们主要学习的内容。
1. 相关类介绍
1.1 SqlSession
1.1.1 SqlSession简介
SqlSession是一个面向用户的接口,定义了操作数据库的方法,例如:selectList, selectOne等等。
每个线程都应该有自己的SqlSession对象,它不能共享使用,也是线程不安全的。因此最佳的使用范围是在请求范围内、或者方法范围内,绝不能将SqlSession放到静态属性中。
SqlSession使用原则:要做到SqlSession:随用随取,用完就关,一定要关
1.1.2 SqlSession的常用API
SqlSession操作数据库的常用方法有:
方法 | 作用 |
---|---|
selectList(String statement, Object param) | 查询多条数据,封装JavaBean集合 |
selectOne(String statement, Object param) | 查询一条数据,封装JavaBean对象 查询一个数据,比如查询数量 |
insert(String statement, Object param) | 添加数据,返回影响行数 |
update(String statement, Object param) | 修改数据,返回影响行数 |
delete(String statement, Object param) | 删除数据,返回影响行数 |
-
以上方法中的参数statment,是映射文件中的namespace 和 id的值方法名组成的。
-
例如:
映射文件的namespace值为
com.guang.dao.UserDao
,执行的方法名是queryAll
那么statement的值就是:
com.guang.dao.UserDao.queryAll
1.2 SqlSessionFactory
是一个接口,定义了不同的openSession()方法的重载。SqlSessionFactory一旦创建后,可以重复使用,通常是以单例模式管理。
SqlSessionFactory使用原则:单例模式管理,一个应用中,只要有一个SqlSessionFactory对象即可。
1.3 SqlSessionFactoryBuilder
用于构建SqlSessionFactory工厂对象的。一旦工厂对象构建完成,就不再需要SqlSessionFactoryBuilder了,通常是作为工具类使用。
SqlSessionFactoryBuilder:只要生产了工厂,builder对象就可以垃圾回收了
2. 需求说明
针对user表进行CURD操作,要求使用映射器接口实现类的方式实现:
- 查询全部用户,得到
List<User>
- 保存用户(新增用户)
- 修改用户
- 删除用户
- 根据主键查询一个用户,得到
User
- 模糊查询
- 查询数量
3. 示例代码
3.1 创建Java项目,准备JavaBean
1) 创建Java项目,导入依赖
2) 创建JavaBean
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//get/set...
//toString...
}
3.2 准备Mybatis的映射器和配置文件
1) 创建映射器接口UserDao(暂时不需要增加方法,备用)
public interface UserDao{
List<User> queryAll();
void save(User user);
void edit(User user);
void delete(Integer id);
}
2) 创建映射器接口的实现类UserDaoImpl
public class UserDaoImpl implements UserDao{
private SqlSessionFactory factory;
/**
* 构造方法。因为工厂对象,是整个应用只要一个就足够了,所以这里不要创建SqlSessionFactory对象
* 而是接收获取到工厂对象来使用。
*/
public UserDaoImpl(SqlSessionFactory factory) {
this.factory = factory;
}
@Override
public List<User> queryAll() {
SqlSession session = factory.openSession();
List<User> users = session.selectList("com.guang.dao.UserDao.queryAll");
session.close();
return users;
}
@Override
public void save(User user) {
SqlSession session = factory.openSession();
session.insert("com.guang.dao.UserDao.save", user);
session.commit();
session.close();
}
@Override
public void edit(User user) {
SqlSession session = factory.openSession();
session.update("com.guang.dao.UserDao.edit", user);
session.commit();
session.close();
}
@Override
public void delete(Integer id) {
SqlSession session = factory.openSession();
session.delete("com.guang.dao.UserDao.delete", id);
session.commit();
session.close();
}
}
3) 创建映射文件UserDao.xml(暂时不需要配置statement,备用)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guang.dao.UserDao">
<select id="queryAll" resultType="User">
select * from user
</select>
<insert id="save" parameterType="User">
<selectKey resultType="int" keyProperty="id" order="AFTER">
select last_insert_id()
</selectKey>
insert into user (id, username, birthday, address, sex)
values (#{id}, #{username}, #{birthday},#{address},#{sex})
</insert>
<update id="edit" parameterType="User">
update user set username = #{username}, birthday = #{birthday},
address = #{address}, sex = #{sex} where id = #{id}
</update>
<delete id="delete" parameterType="int">
delete from user where id = #{uid}
</delete>
</mapper>
4) 创建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>
<typeAliases>
<package name="com.guang.pojo"/>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/guang/dao/UserDao.xml"/>
</mappers>
</configuration>
4) 准备log4j.properties日志配置文件
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
3.3 准备单元测试类
public class MybatisDaoCURDTest {
private InputStream is;
private SqlSessionFactory factory;
private UserDao dao;
@Test
public void testQueryAll(){
List<User> users = dao.queryAll();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void testSaveUser(){
User user = new User();
user.setUsername("tom");
user.setAddress("广东深圳");
user.setBirthday(new Date());
user.setSex("男");
System.out.println("保存之前:" + user);
dao.save(user);
System.out.println("保存之后:" + user);
}
@Test
public void testEditUser(){
User user = new User();
user.setId(71);
user.setUsername("jerry");
user.setAddress("广东深圳宝安");
user.setSex("女");
user.setBirthday(new Date());
dao.edit(user);
}
@Test
public void testDeleteUser(){
dao.delete(71);
}
@Before
public void init() throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(is);
//创建Dao实现类对象时,把factory作为构造参数传递进去---一个应用只要有一个factory就够了
dao = new UserDaoImpl(factory);
}
@After
public void destory() throws IOException {
is.close();
}
}
总结
-
搭建Mybatis的开发环境
- 在dao包里创建映射器接口,在接口里写方法
- 创建映射文件,里边写方法对应的sql语句
- 映射文件名称,必须和 映射器名称相同
- 映射文件位置,必须和 映射器位置相同
- 创建全局配置文件
- 配置数据库环境
- 配置所有映射器的位置
-
如果要使用Mybatis实现一个功能,只要两步:
- 在映射器里增加方法
- 在映射文件里增加配置
-
映射文件里的内容:
-
select
标签:配置查询- id:对应的方法名称
- parameterType:方法参数的类型,可以省略
- resultType:查询结果集封装的类型
-
insert
:配置插入- id:对应的方法名称
- parameterType:方法参数的类型,可以省略
- 如果要插入时获取最新的主键值
<insert> <selectKey keyProperty="id" resultType="int" order="AFTER"> select last_insert_id() </selectKey> </insert>
-
update
:配置修改- id:对应的方法名称
- parameterType:方法参数的类型,可以省略
-
delete
:配置删除- id:对应的方法名称
- parameterType:方法参数的类型,可以省略
-
-
parameterType:
- 如果一个参数:
- 如果是简单类型,
#{随意写}
- 如果是POJO,
#{POJO的属性名}
- 如果是Map,
#{map的key}
- 如果是List,
#{list[索引]}
- 如果是简单类型,
- 如果多个参数:
- 默认情况下:
#{arg0}, #{arg1}, ...
#{param1}, #{param2},...
- 使用注解@Param指定参数名称
#{参数名称}
#{param1}, #{param2}, ...
- 默认情况下:
- 如果一个参数:
注意使用开发规范!!!!!!!!!!!!
使用foreach标签的时候要注意!!!!!!!
mybatis将自动提交设置为关闭状态!!!!上面有分析
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?