简介:
Mybatis是什么?
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis,实质上Mybatis对ibatis进行一些改进。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
Mybatis框架原理(核心)
- mybatis配置文件,包括Mybatis全局配置文件和Mybatis映射文件,其中全局配置文件配置了数据源、事务等信息;映射文件配置了SQL执行相关的 信息。
- mybatis通过读取配置文件信息(全局配置文件和映射文件),构造出SqlSessionFactory,即会话工厂。
- 通过SqlSessionFactory,可以创建SqlSession即会话。Mybatis是通过SqlSession来操作数据库的。
- SqlSession本身不能直接操作数据库,它是通过底层的Executor执行器接口来操作数据库的。Executor接口有两个实现类,一个是普通执行器,一个是缓存执行器(默认)。
- Executor执行器要处理的SQL信息是封装到一个底层对象MappedStatement中。该对象包括:SQL语句、输入参数映射信息、输出结果集映射信息。其中输入参数和输出结果的映射类型包括java的简单类型、HashMap集合对象、POJO对象类型。
操作测试
环境搭建
下载mybaits包:https://github.com/mybatis/mybatis-3/releases
解压后的mybaits目录:
工程搭建(三步)
第一步:创建java工程
用eclipse创建一个java工程,jdk使用1.8+,并创建3个source folder,src用来放源码,config用来放全局的配置文件,test用来测试:
第二步:在项目下创建lib目录,加入jar包
加入以下四部分jar包,其中junit的jar包,是非必须的。
Mybatis核心包
Mybatis依赖包
MySQL驱动包
Junit单元测试包(单元测试需要的包)
添加之后的lib包目录:
第三步:添加log4j.properties文件
Mybatis使用的日志包是log4j的,所以需要添加log4j.properties。
文件内容可以从mybatis目录中的pdf中拷贝
在classpath下创建log4j.properties如下:
日志级别在开发阶段设置成DEBUG,在生产阶段设置成INFO或者ERROR。
数据库环境搭建:
/*Table structure for table `user` */
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用户名称',
`birthday` DATE DEFAULT NULL COMMENT '生日',
`sex` CHAR(1) DEFAULT NULL COMMENT '性别',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
/*Table structure for table `items` */
CREATE TABLE `items` (
`id` INT(11) NOT NULL AUTO_INCREMENT,/*自增主键*/
`name` VARCHAR(32) NOT NULL COMMENT '商品名称',
`price` FLOAT(10,1) NOT NULL COMMENT '商品定价',
`detail` TEXT COMMENT '商品描述',
`pic` VARCHAR(64) DEFAULT NULL COMMENT '商品图片',
`createtime` DATETIME NOT NULL COMMENT '生产日期',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
/*Table structure for table `orders` */
CREATE TABLE `orders` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL COMMENT '下单用户id',
`number` VARCHAR(32) NOT NULL COMMENT '订单号',
`createtime` DATETIME NOT NULL COMMENT '创建订单时间',
`note` VARCHAR(100) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
KEY `FK_orders_1` (`user_id`),
CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=INNODB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
/*Table structure for table `orderdetail` */
CREATE TABLE `orderdetail` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`orders_id` INT(11) NOT NULL COMMENT '订单id',
`items_id` INT(11) NOT NULL COMMENT '商品id',
`items_num` INT(11) DEFAULT NULL COMMENT '商品购买数量',
PRIMARY KEY (`id`),
KEY `FK_orderdetail_1` (`orders_id`),
KEY `FK_orderdetail_2` (`items_id`),
CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
插入数据:
/*Data for the table `user` */
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'王五',NULL,'2',NULL),(10,'张三','2014-07-10','1','北京市'),(16,'张小明',NULL,'1','河南郑州'),(22,'陈小明',NULL,'1','河南郑州'),(24,'张三丰',NULL,'1','河南郑州'),(25,'陈小明',NULL,'1','河南郑州'),(26,'王五',NULL,NULL,NULL);
/*Data for the table `items` */
insert into `items`(`id`,`name`,`price`,`detail`,`pic`,`createtime`) values (1,'台式机',3000.0,'该电脑质量非常好!!!!',NULL,'2015-02-03 13:22:53'),(2,'笔记本',6000.0,'笔记本性能好,质量好!!!!!',NULL,'2015-02-09 13:22:57'),(3,'背包',200.0,'名牌背包,容量大质量好!!!!',NULL,'2015-02-06 13:23:02');
/*Data for the table `orders` */
insert into `orders`(`id`,`user_id`,`number`,`createtime`,`note`) values (3,1,'1000010','2015-02-04 13:22:35',NULL),(4,1,'1000011','2015-02-03 13:22:41',NULL),(5,10,'1000012','2015-02-12 16:13:23',NULL);
/*Data for the table `orderdetail` */
insert into `orderdetail`(`id`,`orders_id`,`items_id`,`items_num`) values (1,3,1,1),(2,3,2,3),(3,4,3,4),(4,4,2,3);
表关系如下:
创建全局配置文件
在config目录下,创建SqlMapConfig.xml文件,该名称不是固定不变的。
内容可以从pdf中复制
在config目录下创建db.properties文件,然后配置好数据库连接信息,并在SqlMapConfig.xml中添加properties标签加载数据库配置文件:
基础测试:
实现用户的增删改查功能
在config下创建类映射文件User.xml:
根据id查询用户 :
创建pojo类(每个字段都私有,并且有标准的get,set方法):
package mybaits_test.po;
import java.util.Date;
public class User {
private int id;
private String username;
private Date birthday;
private char sex;
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 Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address="
+ address + "]";
}
}
测试代码:
输出结果:
User [id=10, username=张三, birthday=Thu Jul 10 00:00:00 CST 2014, sex=1, address=北京市]
可以看到名成相同的字段被自动赋值
添加用户,删除用户,修改用户信息
映射文件:
User.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">
<mapper namespace="test">
<select id="findUserById" parameterType="int" resultType="mybaits_test.po.User">
SELECT * FROM USER WHERE id=#{id}
</select>
<insert id="insertUser" parameterType="mybaits_test.po.User" >
INSERT INTO USER (username,sex,address) VALUE(#{username},#{sex},#{address})
</insert>
<update id="updateUser" parameterType="int">
UPDATE USER SET username='yan' where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
DELETE FROM USER WHERE id=#{id}
</delete>
</mapper>
测试代码:
package mybaits_test;
import java.io.IOException;
import java.io.InputStream;
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.jupiter.api.Test;
import mybaits_test.po.User;
public class Test01 {
@Test
public void testFindUserById() throws IOException {
//获取SqlSession
String sqlMapConfigPath="SqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory=builder.build(in);
SqlSession sqlSession=sqlSessionFactory.openSession();
int id=10;
User user = sqlSession.selectOne("test.findUserById", id);
System.out.println(user);
}
@Test
public void testInsertUser() throws IOException {
//获取SqlSession
String sqlMapConfigPath="SqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory=builder.build(in);
SqlSession sqlSession=sqlSessionFactory.openSession();
User user=new User();
user.setUsername("闫绍帅");
user.setSex('2');
user.setAddress("合肥长丰");
sqlSession.insert("test.insertUser", user);
sqlSession.commit();
}
@Test
public void testUpdateUser() throws IOException {
//获取SqlSession
String sqlMapConfigPath="SqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory=builder.build(in);
SqlSession sqlSession=sqlSessionFactory.openSession();
sqlSession.update("test.updateUser", 29);
sqlSession.commit();
}
@Test
public void testDeleteUser() throws IOException {
//获取SqlSession
String sqlMapConfigPath="SqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory=builder.build(in);
SqlSession sqlSession=sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUser", 29);
sqlSession.commit();
}
}
模糊查询
#{}和${}区别
#{}表示占位符?,#{}接收简单类型的参数时,里面的名称可以任意(这里如果用'%#{}%',就相当于'%?%',是不对的)
${}表示拼接符,${}接收简单类型的参数时,里面的名称必须是value
${}里面的值会原样输出,不加解析(如果该参数值是字符串,不会添加引号)
${}存在sql注入的风险,但是有些场景下必须使用,比如排序后面会动态传入排序的列名
插入之主键返回:
mysql
(1)利用自增主键则使用 SELECT LAST_INSERT_ID()函数查询
public void testInsertUserRTId() throws IOException {
//获取SqlSession
String sqlMapConfigPath="SqlMapConfig.xml";
InputStream in=Resources.getResourceAsStream(sqlMapConfigPath);
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory=builder.build(in);
SqlSession sqlSession=sqlSessionFactory.openSession();
User user=new User();
user.setUsername("闫绍帅");
user.setSex('2');
user.setAddress("合肥长丰");
sqlSession.insert("test.insertUserRTId", user);
System.out.println("id="+user.getId());
sqlSession.commit();
}
(2)使用mysql随机主键则使用SELECT UUID()
Oracle
序列也就是sequence,它是Oracle的主键生成策略
Mapper代理的开发dao
即开发mapper接口(相当于代理dao接口)
Mapper代理使用的是jdk的代理策略。
Mapper代理的开发规范
- mapper接口的全限定名要和mapper映射文件的namespace值一致。
- mapper接口的方法名称要和mapper映射文件的statement的id一致。
- mapper接口的方法参数类型要和mapper映射文件的statement的parameterType的值一致,而且它的参数是一个。
- mapper接口的方法返回值类型要和mapper映射文件的statement的resultType的值一致。
src下新建dao包,下面新建一个UserDao接口,添加若干方法
config下新建UserMapper.xml文件,各个对应关系如下,在SqlMapConfig.xml中添加User Mapper.xml文件的映射
各个对应关系如下:
接口:
映射文件:
测试代码:
还可以这样写
在全局配置文件中加载mapper文件的写法:
<mapper resource=""/>的写法:
使用相对于类路径的资源,用于加载单个的配置文件
如:<mapper resource="sqlmap/User.xml" />
<mapper url=""/>
使用完全限定路径
如:<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
<mapper class=""/>
使用mapper接口的全限定名
如:<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口和mapper映射文件要名称相同,且放到同一个目录下;
<package name=""/>(推荐)
注册指定包下的所有映射文件,
如:<package name="cn.itcast.mybatis.mapper"/>
注意:此种方法要求mapper接口和mapper映射文件要名称相同,且放到同一个目录下;
改用推荐方式加载映射文件:
映射文件UserMapper.xml:
映射接口:
目录结构:
加载映射文件:
测试代码:
全局配置文件
概览
SqlMapConfig.xml的配置内容和顺序如下(顺序不能乱):
Properties(属性)
Settings(全局参数设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境信息集合)
environment(单个环境信息)
transactionManager(事物)
dataSource(数据源)
mappers(映射器)
常用配置
Properties
Db.properties
SqlMapConfig.xml
加载的顺序
- 先加载properties中property标签声明的属性
- 再加载properties标签引入的java配置文件中的属性
- parameterType的值会和properties的属性值发生冲突。
settings
mybatis全局配置参数,全局参数将会影响mybatis的运行行为。
typeAliases
对po类进行别名的定义
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 |
自定义别名
映射文件:
输入映射:
1.简单类型
2.po类映射
3.嵌套po类映射
Map
同传递POJO对象一样,map的key相当于pojo的属性名,值相当于属性值。
resultType
使用要求
使用resultType进行结果映射时,需要查询出的列名和映射的对象的属性名一致,才能映射成功。
如果查询的列名和对象的属性名全部不一致,那么映射的对象为空。
如果查询的列名和对象的属性名有一个一致,那么映射的对象不为空,但是只有映射正确那一个属性才有值。
如果查询的sql的列名有别名,那么这个别名就是和属性映射的列名(即根据别名去匹配)。
简单类型
注意,对简单类型的结果映射也是有要求的,查询的列必须是一列,才能映射为简单类型。
查询用户总数:
配置文件
测试
接口
Pojo对象和pojo列表
查询所有用户列表:
resultMap
使用要求
使用resultMap进行结果映射时,不需要查询的列名和映射的属性名必须一致。但是需要声明一个resultMap,来对列名和属性名进行映射。
动态sql
在mybatis中,它提供了一些动态sql标签,可以让程序员更快的进行mybatis的开发,这些动态sql可以通过sql的可重用性。。
常用的动态sql标签:if标签、where标签、sql片段、foreach标签
If标签/where标签
Sql片段
Sql片段可以让代码有更高的可重用性
Sql片段需要先定义后使用
Foreach标签
可以循环传入参数值
根据用户ID集合进行查询
SELECT * FROM USER WHERE id IN (?,?,?)