1.1mybatis下载
mybaits 的代码由github.com 管理,
地址:https://github.com/mybatis/mybatis-3/releases
mybatis-3.4.6.jar----mybatis 的核心包
lib----mybatis 的依赖包
mybatis-3.4.6.pdf----mybatis 使用手册
1.2创建mysql 数据库
1.3Mybatis 入门程序
1.3.1需求
实现以下功能:
根据用户id 查询一个用户信息
根据用户名称模糊查询用户信息列表
添加用户
更新用户
删除用户
1.3.2 第一步:创建java 工程
使用eclipse 创建java 工程,jdk 使用jdk1.8.0_144
1.3.3 第二步:加入jar 包
加入mybatis 核心包、依赖包、数据驱动包。
1.3.4 第三步:log4j.properties
在classpath 下创建log4j.properties
如下:
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
mybatis 默认使用log4j 作为输出日志信息。
1.3.5 第四步:SqlMapConfig.xml
在classpath 下创建SqlMapConfig.xml,如下:
SqlMapConfig.xml 是mybatis 核心配置文件,上边文件的配置内容为数据源、事务管理。
1.3.6 第五步:po 类
Po 类作为mybatis 进行sql 映射使用,po 类通常与数据库表对应,User.java 如下:
package com.ghw.po;
import java.util.Date;
public class User {
private int id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
1.3.7 第六步:程序编写
1.3.7.1查询
1.3.7.1.1 映射文件:
在classpath 下的sqlmap 目录下创建sql 映射文件Users.xml:
namespace :命名空间,用于隔离sql 语句,后面会讲另一层非常重要的作用。
在Users.xml 中添加:
parameterType:定义输入到sql 中的映射类型,#{id}表示使用preparedstatement 设
置占位符号并将输入变量id 传到sql。
resultType:定义结果映射类型。
1.3.7.1.2 加载映射文件
mybatis 框架需要加载映射文件,将Users.xml 添加在SqlMapConfig.xml,如下:
1.3.7.1.3 测试程序:
package com.ghw.fitst;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.Logger;
import org.junit.Test;
import com.ghw.po.User;
public class Mybatis_Test {
static Logger logger = Logger.getLogger(Mybatis_Test.class);
@Test
public void findUserByIdTest() throws IOException {
// 读取mybatis配置文件
String resource = "SqlMapConfig.xml";
// 得到配置文件流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建会话工厂,传入mybatis配置文件流
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过工厂得到SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession操作数据库
// 第一个参数:映射文件中statement的id,等于=namespace+"."+statement的id
// 第二个参数:指定和映射文件中所匹配的parameterType类型的参数
// sqlSession.selectOne结果 是与映射文件中所匹配的resultType类型的对象
// selectOne查询出一条记录
User user = sqlSession.selectOne("test.findUserById", 1);
// 输出查询到的结果
logger.info(user);
}
@Test
public void findUserByNameTest() throws IOException {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List list = sqlSession.selectList("test.findUserByusername", "小明");
logger.info(list);
}
}
1.3.7.1.4 #{}和${}
#{}
表示一个占位符号,通过#{}
可以实现preparedStatement
向占位符中设置值,自动进行java
类型和jdbc
类型转换,#{}
可以有效防止sql
注入。#{}
可以接收简单类型值或pojo
属性值。
如果parameterType
传输单个简单类型值,#{}
括号中可以是value
或其它名称。
${}
表示拼接sql
串,通过${}
可以将parameterType
传入的内容拼接在sql
中且不进行jdbc
类型转换, ${}
可以接收简单类型值或pojo
属性值,如果parameterType
传输单个简单类型值,${}
括号中只能是value
。可能引起sql注入
1.3.7.1.5 parameterType 和resultType
parameterType:指定输入参数类型,mybatis 通过ognl 从输入对象中获取参数值拼接在sql中。
resultType:指定输出结果类型,mybatis 将sql 查询结果的一行记录数据映射为resultType指定类型的对象。
1.3.7.1.6 selectOne 和selectList
selectOne 查询一条记录,如果使用selectOne 查询多条记录则抛出异常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result(or null) to be returned by selectOne(), but found: 3
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultS
qlSession.java:70)
selectList 可以查询一条或多条记录。
1.3.7.2添加
1.3.7.2.1 映射文件:
在SqlMapConfig.xml 中添加:
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
1.3.7.2.2 测试程序:
// 添加用户
public void insertUser() {
User user1 = new User();
user1.setUsername("李书豪");
user1.setBirthday(new Date());
user1.setSex("男");
user1.setAddress("陕西渭南");
//在@before中已经获取了sqlSession
sqlSession.insert("test.insertUser", user1);
//提交事务
sqlSession.commit();
}
1.3.7.2.3 mysql 自增主键返回
通过修改sql 映射文件,可以将mysql 自增主键返回:
select LAST_INSERT_ID()
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
添加selectKey 实现将主键返回
keyProperty:返回的主键存储在pojo中的哪个属性
order:selectKey 的执行顺序,是相对与insert 语句来说,由于mysql 的自增原理执行完insert 语句之后才将主键生成,所以这里selectKey 的执行顺序为after
resultType:返回的主键是什么类型
LAST_INSERT_ID():是mysql 的函数,返回auto_increment 自增列新记录id 值。
1.3.7.2.4 Mysql 使用uuid 实现主键
需要增加通过select uuid()得到uuid 值
select uuid()
insert into user(id,username,birthday,sex,address)
values(#{id},#{username},#{birthday},#{sex},#{address})
注意这里使用的order 是“BEFORE”
1.3.7.2.5 Oracle 使用序列生成主键
首先自定义一个序列且用于生成主键,selectKey 使用如下:
SELECT 自定义序列.NEXTVAL FROM DUAL
insert into user(id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})
注意这里使用的order
是BEFORE
1.3.7.3删除
1.3.7.3.1 映射文件:
delete from user where id = #{id}
1.3.7.3.2 测试程序:
// 删除用户
@Test
public void deleteUser() {
// 删除编号32的用户
sqlSession.delete("test.deleteUser", 32);
// 提交事务
sqlSession.commit();
logger.info("删除成功");
}
1.3.7.4修改
1.3.7.4.1 映射文件
update user set
username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id = #{id}
1.3.7.4.2 测试程序
// 修改用户
@Test
public void updateUser() {
User user2 = new User();
user2.setId(26);
user2.setBirthday(new Date());
user2.setSex("男");
user2.setUsername("李书豪2");
user2.setAddress("西安邮电大学");
sqlSession.update("test.updateUser", user2);
sqlSession.commit();
logger.info("修改成功");
}
1.3.8 Mybatis 解决jdbc 编程的问题
- 数据库链接频繁建立与释放链接,造成系统资源浪费。
解决:在SqlMapConfig.xml
中配置数据链接池,使用连接池管理数据库链接。 - sql语句写在java代码中,修改的时候要修改源代码,不利于后期维护升级。
解决:将sql语句配置在XXXXmapper.xml 文件中与java 代码分离。 - 向sql 语句传参数麻烦,因为sql 语句的where 条件不一定,可能多也可能少,占位符需要和参数一一对应。
解决:Mybatis
自动将java 对象映射至sql
语句,通过statement
中的parameterType
定义输入参数的类型。 - 对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo 对象解析比较方便。
解决:Mybatis 自动将sql 执行结果映射至java 对象,通过statement 中的resultType 定义输出结果的类型。
1.3.9 与hibernate 不同
- Mybatis 和hibernate 不同,它不完全是一个ORM 框架,因为MyBatis 需要程序员自己编写Sql 语句,不过mybatis 可以通过XML 或注解方式灵活配置要运行的sql 语句,并将java对象和sql 语句映射生成最终执行的sql,最后将sql 执行的结果再映射生成java 对象。
- Mybatis 学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql 执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。
- Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate 开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R 映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate 需要具有很强的经验和能力才行。
- 总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。
2 Dao 开发方法
使用Mybatis 开发Dao,通常有两个方法,即原始Dao开发方法和Mapper接口开发方法。
2.1 需求
将下边的功能实现Dao:
- 根据用户id 查询一个用户信息
- 根据用户名称模糊查询用户信息列表
- 添加用户信息
2.2 SqlSession 的使用范围
SqlSession 中封装了对数据库的操作,如:查询、插入、更新、删除等。通过SqlSessionFactory 创建SqlSession,而SqlSessionFactory 是通过SqlSessionFactoryBuilder进行创建。
2.2.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 用于创建SqlSessionFacoty,SqlSessionFacoty 一旦创建完成就不需要SqlSessionFactoryBuilder 了,因为SqlSession 是通过SqlSessionFactory 生产,所以可以将SqlSessionFactoryBuilder 当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
2.2.2 SqlSessionFactory
SqlSessionFactory是一个接口,接口中定义了openSession 的不同重载方法SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。
2.2.3 SqlSession
SqlSession 是一个面向用户的接口, sqlSession 中定义了数据库操作, 默认使用
DefaultSqlSession 实现类。
执行过程如下:
- 加载数据源等配置信息
Environment environment = configuration.getEnvironment(); - 创建数据库链接
- 创建事务对象
- 创建Executor,SqlSession 所有操作都是通过Executor 完成,mybatis 源码如下:
if (ExecutorType.BATCH == executorType) {
executor = newBatchExecutor(this, transaction);
} elseif (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor, autoCommit);
}
- SqlSession 的实现类即DefaultSqlSession,此对象中对操作数据库实质上用的是Executor
结论:
每个线程都应该有它自己的SqlSession 实例。SqlSession 的实例不能共享使用,它也是
线程不安全的。因此最佳的范围是请求或方法范围。绝对不能将SqlSession 实例的引用放在一个类的静态字段或实例字段中。打开一个SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到finally 块中以确保每次都能执行关闭。如下:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
2.3 原始Dao 开发方式
原始Dao 开发方法需要程序员编写Dao 接口和Dao 实现类。
2.3.1 映射文件
select LAST_INSERT_ID()
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
delete from user where id
= #{id}
update user set
username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id = #{id}
2.3.2 Dao 接口
package com.ghw.dao;
import com.ghw.po.User;
public interface UserDao {
// 根据id查询用户
public User getUserById(int id) throws Exception;
// 添加用户
public void insertUser(User user) throws Exception;
// 删除用户
public void deleteUser(int id) throws Exception;
// 修改用户
public void updateUser(User user) throws Exception;
}
Dao实现类
package com.ghw.dao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import com.ghw.po.User;
public class UserDaoImp implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImp(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
// 根据id查询用户
public User getUserById(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
return user;
}
// 添加用户
public void insertUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
}
// 删除用户
public void deleteUser(int id) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUser", id);
sqlSession.commit();
}
// 修改用户
public void updateUser(User user) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.update("test.updateUser", user);
sqlSession.commit();
}
}
Dao测试类
package com.ghw.dao;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import com.ghw.po.User;
public class UserDaoImpTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void before() throws IOException {
// 读取mybatis配置文件
String resource = "SqlMapConfig.xml";
// 得到配置文件流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建会话工厂,传入mybatis配置文件流
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过工厂得到SqlSession
}
// 根据id查询用户测试方法
@Test
public void testGetUserById() throws Exception {
UserDao userDao = new UserDaoImp(sqlSessionFactory);
User user = userDao.getUserById(26);
}
// 添加用户测试方法
@Test
public void testinsertUser() throws Exception {
UserDao userDao = new UserDaoImp(sqlSessionFactory);
User user1 = new User();
user1.setUsername("李书豪");
user1.setBirthday(new Date());
user1.setSex("男");
user1.setAddress("陕西渭南");
userDao.insertUser(user1);
}
// 删除用户测试方法
@Test
public void testdeleteUser() throws Exception {
UserDao userDao = new UserDaoImp(sqlSessionFactory);
userDao.deleteUser(29);
}
// 修改用户测试方法
@Test
public void testupdateUser() throws Exception {
UserDao userDao = new UserDaoImp(sqlSessionFactory);
User user2 = new User();
user2.setId(26);
user2.setBirthday(new Date());
user2.setSex("女");
user2.setUsername("李书豪1");
user2.setAddress("西安邮电大学1");
userDao.updateUser(user2);
}
}
2.3.3 问题
原始Dao 开发中存在以下问题:
- Dao 方法体存在重复代码:通过SqlSessionFactory 创建SqlSession,调用SqlSession 的数据库操作方法
- 调用sqlSession 的数据库操作方法需要指定statement 的id,这里存在硬编码,不
得于开发维护。
2.4 Mapper 动态代理方式
2.4.1 实现原理
Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao 接口实现类方法。
Mapper 接口开发需要遵循以下规范:
- Mapper.xml 文件中的namespace 与mapper 接口的类路径相同。
- Mapper 接口方法名和Mapper.xml 中定义的每个statement 的id 相同
- Mapper 接口方法的输入参数类型和mapper.xml 中定义的每个sql 的parameterType 的类型相同
- Mapper 接口方法的输出参数类型和mapper.xml 中定义的每个sql 的resultType 的类型相同
2.4.2 Mapper.xml(映射文件)
定义mapper 映射文件UserMapper.xml(内容同Users.xml),需要修改namespace 的值为UserMapper 接口路径。将UserMapper.xml 放在classpath 下mapper 目录下。
select LAST_INSERT_ID()
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
delete from user where id
= #{id}
update user set
username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id = #{id}
2.4.3 Mapper.java(接口文件)
package com.ghw.mapper;
import com.ghw.po.User;
public interface UserMapper {
// 根据id查询用户
public User findUserById(int id) throws Exception;
// 自定义条件查询用户列表
public User findUserByusername(String name) throws Exception;
// 添加用户
public void insertUser(User user) throws Exception;
// 删除用户
public void deleteUser(int id) throws Exception;
// 修改用户
public void updateUser(User user) throws Exception;
}
接口定义有如下特点:
- Mapper 接口方法名和Mapper.xml 中定义的statement 的id 相同
- Mapper 接口方法的输入参数类型和mapper.xml 中定义的statement 的parameterType 的类型相同
- Mapper 接口方法的输出参数类型和mapper.xml 中定义的statement 的resultType 的类型相同
2.4.4 加载UserMapper.xml 文件
修改SqlMapConfig.xml 文件:
2.4.5 测试
package com.ghw.mapper;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import com.ghw.po.User;
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void before() throws IOException {
// 读取mybatis配置文件
String resource = "SqlMapConfig.xml";
// 得到配置文件流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建会话工厂,传入mybatis配置文件流
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过工厂得到SqlSession
}
// mapper根据id查询用户测试方法
@Test
public void testfindUserById() throws Exception {
// 获取sqlsession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用代理对象的方法
User user = userMapper.findUserById(26);
// 输出查询到的内容
System.out.println(user);
// 关闭sqlsession
sqlSession.close();
}
// mapper自定义条件查询用户列表
@Test
public void testfindUserByusername() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 通过mapper接口查询用户列表
List list = userMapper.findUserByusername("小明");
System.out.println(list);
// 关闭session
sqlSession.close();
}
// mapper添加用户
@Test
public void testinsertUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
user1.setUsername("李书豪");
user1.setBirthday(new Date());
user1.setSex("男");
user1.setAddress("陕西渭南");
// 通过mapper接口添加用户
userMapper.insertUser(user1);
// 提交
sqlSession.commit();
// 关闭session
sqlSession.close();
}
// mapper删除用户
@Test
public void testdeleteUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 通过mapper接口删除
userMapper.deleteUser(30);
// 提交
sqlSession.commit();
// 关闭session
sqlSession.close();
}
// mapper修改用户
@Test
public void testupdateUser() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user2 = new User();
user2.setId(28);
user2.setBirthday(new Date());
user2.setSex("女");
user2.setUsername("李书豪1");
user2.setAddress("西安邮电大学1");
// 通过mapper接口修改用户
userMapper.updateUser(user2);
// 提交
sqlSession.commit();
// 关闭session
sqlSession.close();
}
}
2.4.6 总结
-
selectOne
和selectList
动态代理对象调用sqlSession.selectOne()
和sqlSession.selectList()
是根据mapper 接口方法的返回值决定,如果返回List
则调用selectList()
方法,如果返回单个对象则调用selectOne()
方法。 -
namespace
mybatis
官方推荐使用mapper
代理方法开发mapper
接口,程序员不用编写mapper
接口实现类,使用mapper
代理方法时,输入参数可以使用pojo
包装对象或map
对象,保证dao
的通用性。
3 SqlMapConfig.xml 配置文件
3.1 配置内容
SqlMapConfig.xml 中配置的内容和顺序如下:
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
3.2 properties(属性)
SqlMapConfig.xml
可以引用java
属性文件中的配置信息如下:
在classpath
(类路径,Source Folder文件夹下就是类路径)下定义db.properties
文件,内容如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis001?characterEncoding=utf-8
jdbc.username=root
jdbc.password=admin
SqlMapConfig.xml
引用如下:
注意: MyBatis 将按照下面的顺序来加载属性:
- 在
properties
元素体内定义的属性首先被读取。 - 然后会读取
properties
元素中resource
或url
加载的属性,它会覆盖已读取的同名属性。 - 最后读取
parameterType
传递的属性,它会覆盖已读取的同名属性。 - 因此,
通过parameterType
传递的属性具有最高优先级,resource
或url
加载的属性次之,最低优先级的是properties
元素体内定义的属性。
3.3 settings(配置)
mybatis
全局配置参数,全局参数将会影响mybatis
的运行行为。
3.4 typeAliases(类型别名)
3.4.1 mybatis 支持别名:
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
3.4.2 自定义别名:
在SqlMapConfig.xml
中配置:
3.5 typeHandlers
(类型处理器)
类型处理器用于java
类型和jdbc
类型映射,如下:
mybatis 自带的类型处理器基本上满足日常需求,不需要单独定义。
mybatis 支持类型处理器:
类型处理器 | Java类型 | JDBC类型 |
---|---|---|
BooleanTypeHandler | Boolean,boolean | 任何兼容的布尔值 |
ByteTypeHandler | Byte,byte | 任何兼容的数字或字节类型 |
ShortTypeHandler | Short,short | 任何兼容的数字或短整型 |
IntegerTypeHandler | Integer,int | 任何兼容的数字和整型 |
LongTypeHandler | Long,long | 任何兼容的数字或长整型 |
FloatTypeHandler | Float,float | 任何兼容的数字或单精度浮点型 |
DoubleTypeHandler | Double,double | 任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler | BigDecimal | 任何兼容的数字或十进制小数类型 |
StringTypeHandler | String | CHAR和VARCHAR类型 |
ClobTypeHandler | String | CLOB和LONGVARCHAR类型 |
NStringTypeHandler | String | NVARCHAR和NCHAR类型 |
NClobTypeHandler | String | NCLOB类型 |
ByteArrayTypeHandler | byte[] | 任何兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB和LONGVARBINARY类型 |
DateTypeHandler | Date(java.util) | TIMESTAMP类型 |
DateOnlyTypeHandler | Date(java.util) | DATE类型 |
TimeOnlyTypeHandler | Date(java.util) | TIME类型 |
SqlTimestampTypeHandler | Timestamp(java.sql) | TIMESTAMP类型 |
SqlDateTypeHandler | Date(java.sql) | DATE类型 |
SqlTimeTypeHandler | Time(java.sql) | TIME类型 |
ObjectTypeHandler | 任意 | 其他或未指定类型 |
EnumTypeHandler | Enumeration类型 | VARCHAR-任何兼容的字符串类型,作为代码存储(而不是索引)。 |
3.6 mappers(映射器)
Mapper 配置的几种方法:
3.6.1
使用相对于类路径的资源
如:
3.6.2
使用完全限定路径
如:
3.6.3
使用mapper 接口类路径
如:
注意:此种方法要求mapper 接口名称和mapper 映射文件名称相同,且放在同一个目录中。
3.6.4
注册指定包下的所有mapper 接口
如:
注意:此种方法要求mapper 接口名称和mapper 映射文件名称相同,且放在同一个目录中。
4 Mapper.xml 映射文件
Mapper.xml 映射文件中定义了操作数据库的sql,每个sql 是一个statement,映射文件是mybatis 的核心。
4.1 parameterType(输入类型)
4.1.1 #{}与${}
#{}
实现的是向prepareStatement 中的预处理语句中设置参数值,sql 语句中#{}表示一个占位
符即?。
使用占位符#{}可以有效防止sql 注入,在使用时不需要关心参数值的类型,mybatis 会自动
进行java 类型和jdbc 类型的转换。#{}可以接收简单类型值或pojo 属性值,如果parameterType
传输单个简单类型值,#{}括号中可以是value 或其它名称。
${}和#{}不同,通过${}可以将parameterType 传入的内容拼接在sql 中且不进行jdbc 类型转
换, ${}可以接收简单类型值或pojo 属性值,如果parameterType 传输单个简单类型值,${}
括号中只能是value。使用${}不能防止sql 注入,但是有时用${}会非常方便,如下的例子:
如果本例子使用#{}则传入的字符串中必须有%号,而%是人为拼接在参数中,显然有点麻烦,如果采用${}在sql 中拼接为%的方式则在调用mapper 接口传递参数就方便很多。
// 如果使用占位符号则必须人为在传参数中加%
List list = userMapper.selectUserByName("%管理员%");
// 如果使用${}原始符号则不用人为在参数中加%
List list = userMapper.selectUserByName("管理员");
再比如order by 排序,如果将列名通过参数传入sql,根据传的列名进行排序,应该写为:
ORDER BY ${columnName}
如果使用#{}将无法实现此功能。
4.1.2 传递简单类型
参考上边的例子。
4.1.3 传递pojo 对象
Mybatis 使用ognl 表达式解析对象字段的值,如下例子:
上边红色标注的是user 对象中的字段名称。
测试:
public void testFindUserByUser() throws Exception {
// 获取session
SqlSession session = sqlSessionFactory.openSession();
// 获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
// 构造查询条件user对象
User user = new User();
user.setId(1);
user.setUsername("管理员");
// 传递user对象查询用户列表
List list = userMapper.findUserByUser(user);
// 关闭session
session.close();
}
异常测试:
Sql
中字段名输入错误后测试,username 输入dusername 测试结果报错:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'dusername' in 'where clause'
### The error may exist in mapper/UserMapper.xml
### The error may involve com.ghw.mapper.UserMapper.findUserByusername-Inline
### The error occurred while setting parameters
4.1.4 传递pojo 包装对象
开发中通过pojo 传递查询条件,查询条件是综合的查询条件,不仅包括用户查询条件
还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),这时可以使用包装对象传递输入参数。
4.1.4.1定义包装对象
定义包装对象将查询条件(pojo)以类组合的方式包装起来。
public class QueryVo {
private User user;
//自定义用户扩展类
private UserCustom userCustom;
4.1.4.2mapper.xml 映射文件
说明:mybatis 底层通过ognl 从pojo 中获取属性值:#{user.username},user 即是传入的包装对象的属性。queryVo 是别名,即上边定义的包装对象类型。
说明:mybatis 底层通过 ognl 从 pojo 中获取属性值:#{user.username},user 即是传入的包 装对象的属性。queryVo 是别名,即上边定义的包装对象类型。
4.1.5 传递hashmap
Sql 映射文件定义如下:
上面的id和username是hashmap的key
测试:
public void testFindUserByHashmap() throws Exception {
// 获取session
SqlSession session = sqlSessionFactory.openSession();
// 获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
// 构造查询条件Hashmap对象
HashMap map = new HashMap();
map.put("id", 1);
map.put("username", "管理员");
// 传递Hashmap对象查询用户列表 Listlist = userMapper.findUserByHashmap(map);
// //关闭session session.close();
}
异常测试: 传递的 map 中的 key 和 sql 中解析的 key 不一致。 测试结果没有报错,只是通过 key 获取值为空。
4.2 resultType(输出类型)
4.2.1输出简单类型
参考 getnow 输出日期类型,看下边的例子输出整型:
Mapper.xml 文件
Mapper 接口
public int findUserCount(User user) throws Exception;
调用:
public void testFindUserCount() throws Exception {
// 获取session
SqlSession session = sqlSessionFactory.openSession();
// 获取mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = new User();
user.setUsername("管理员");
// 传递Hashmap对象查询用户列表
int count = userMapper.findUserCount(user);
// 关闭session session.close();
}
总结: 输出简单类型必须查询出来的结果集有一条记录,最终将第一个字段的值转换为输出类型。 使用 session 的 selectOne 可查询单条记录。
4.2.2输出 pojo 对象
参考 findUserById 的定义:
Mapper.xml
Mapper 接口:
public User findUserById(int id) throws Exception;
测试:
public void testFindUserById() throws Exception {
// 获取session
SqlSession session = sqlSessionFactory.openSession();
// 获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
// 通过mapper接口调用statement
User user = userMapper.findUserById(1);
System.out.println(user);
// 关闭session
session.close();
}
使用 session 调用 selectOne 查询单条记录。
4.2.3输出 pojo 列表
参考 selectUserByName 的定义:
Mapper.xml
Mapper 接口:
public List
测试:
public void testFindUserByUsername() throws Exception {
// 获取session
SqlSession session = sqlSessionFactory.openSession();
// 获限mapper接口实例
UserMapper userMapper = session.getMapper(UserMapper.class);
// 如果使用占位符号则必须人为在传参数中加%
// List list = userMapper.selectUserByName("%管理员%");
// 如果使用${}原始符号则不用人为在参数中加%
List list = userMapper.findUserByUsername("管理员");
// 关闭session
session.close();
}
使用 session 的 selectList 方法获取 pojo 列表。
4.2.4 resultType 总结:
输出 pojo 对象和输出 pojo 列表在 sql 中定义的 resultType 是一样的。 返回单个pojo 对象要保证 sql 查询出来的结果集为单条,内部使用 session.selectOne 方法调用,mapper 接口使用 pojo 对象作为方法返回值。
返回 pojo 列表表示查询出来的结果集可能为多条,内部使用 session.selectList 方法,mapper 接口使用 List
4.2.5 输出 hashmap
输出 pojo 对象可以改用 hashmap 输出类型,将输出的字段名称作为 map 的 key,value 为字 段值。
4.3 resultMap
resultType 可以指定 pojo 将查询结果映射为 pojo,但需要 pojo 的属性名和 sql 查询的列 名一致方可映射成功。 如果 sql 查询字段名和 pojo 的属性名不一致,可以通过 resultMap 将字段名和属性名作 一个对应关系 ,resultMap 实质上还需要将查询结果映射到 pojo 对象中。 resultMap 可以实现将查询结果映射为复杂类型的 pojo,比如在查询结果映射对象中包 括 pojo 和 list 实现一对一查询和一对多查询。
4.3.1 Mapper.xml 定义
使用 resultMap 指定上边定义的 personmap。
4.3.2定义 resultMap
由于上边的 mapper.xml 中 sql 查询列和 Users.java 类属性不一致,需要定义 resultMap: userListResultMap 将 sql 查询列和 Users.java 类属性对应起来
Property:表示 person 类的属性。
Column:表示 sql 查询出来的字段名。
Column 和 property 放在一块儿表示将 sql 查询出来的字段映射到指定的 pojo 类属性上。
4.3.3 Mapper 接口定义
public List
4.4 动态 sql(重点)
通过 mybatis 提供的各种标签方法实现动态拼接 sql。