Mybatis
学习目标
Mybatis 框架介绍
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
Mybatis不是一个完全的orm框架,Mybatis需要程序员自己写sql,但是也存在映射(输入参数映射,输出结果映射),学习门槛mybatis比hibernate低;同时灵活性高,特别适用于业务模型易变的项目,使用范围广。
简单概括:
更加简化jdbc代码,简化持久层,sql语句从代码中分离,利用反射,将表中数据与java bean 属性一一映射 即 ORM(Object Relational Mapping 对象关系映射)
使用范围:
在日常的开发项目中,如中小型项目,例如ERP,需求与关系模型相对固定建议使用Hibernate,对于需求不固定的项目,比如:互联网项目,建议使用mybatis,因为需要经常灵活去编写sql语句。总之,mybatis成为当下必须学习掌握的一个持久层框架。
MyBatis 功能架构图:
从上面我们可以看到mybatis的功能架构分为三层:
API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层接收到调用请求就会调用数据处理层来完成具体的数据处理。
数据处理层:负责具体的SQL查 找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
Mybatis特点
属于持久层ORM框架
持久层: 讲内存中对象数据,转移到数据库中的过程持久层
Mybatis Hibernate Spring-jpa
ORM Object Relational Mapping 对象关系映射框架
类 表
属性 字段
对象 记录
半自化 自动化
Mybatis 半自动化
表需要手动进行设计
提供sql
依赖与数据库平台
优点:学习使用简单(基与原声jdbc封装),优化灵活,适合做互联网项目
Hibernate 自动化ORM框架
表可以通过框架自动创建
省略一些基本的sql
不依赖与数据库平台
缺点: 学生成本高,优化难度大,适合与传统框(OA|图书管理系统...),不适合做大型互联网项目
Mybatis环境搭建
官网: https://mybatis.org/mybatis-3/zh/index.html
新建Java项目
新建Java项目,项目下新建lib目录,存放入jar包
导入jar包
mybatis核心jar包
mybatis依赖jar包
数据库驱动jar包
Add as Library
选中所有的jar包,右键Add as Library\ 管理外部的jar资源
Mybatis配置文件
mybatis提供两种配置文件, 核心配置文件 mybatis-config.xml|mybatis.xml 与 SQL映射文件mapper.xml
核心配置文件添加
是一个xml文件,命名无要求,位置无要求,一般成为mybatis.xml,放在src路径下
dtd
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
mybatis.xml文件配置内容
<?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>
<!--
用于指明使用哪一个开发环境
default : 用于指定使用的环境的id属性值
-->
<environments default="ev">
<!-- 用户配置开发环境 id: 环境的唯一标识 -->
<environment id="ev">
<!--
事务管理器
JBDC : 表示采用JDBC一样的事务管理方式
-->
<transactionManager type="JDBC"/>
<!--
用于配置数据库连接吃和数据库连接参数
POOLED : 表示mybatis采用连接池技术
-->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<property name="username" value="SCOTT"/>
<property name="password" value="TIGER"/>
</dataSource>
</environment>
</environments>
<!-- SQL映射文件配置 -->
<mappers>
<!-- 指明SQL映射文件路径 resource : 包路径 com/.../xxxMapper.xml-->
<mapper resource="com/yjxxt/mappers/UserMapper.xml"/>
</mappers>
</configuration>
Mybatis SQL映射文件
在Mybatis中,推荐使用mappers作为包名,我们只需要写一个映射配置文件就可以,UserMapper.xml,用于定义要执行的sql语句,同时可以设置参数|返回值结果类型
<?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: 命名空间
-->
<mapper namespace="com.yjxxt.mappers.UserMapper">
<!--
查询标签: select 用于编写查询语句
id : 当前文件中保证唯一
resultType : 结果的类型
parameterType : 入参类型
-->
<select id="queryAll" resultType="com.yjxxt.pojo.User">
select * from t_user
</select>
</mapper>
注意:不要忘记mybatis核心xml文件中的mapper配置
测试
public class TestUser {
public static void main(String[] args) throws IOException {
//1.加载mybatis全局核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis.xml");
//2.构建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂获取会话SqlSession
SqlSession session = factory.openSession();
//4.通过session调用方法执行查询
//selectList() 查到的数据返回一个list集合,没查到返回空的list
//selectList 的第一个参数为statement: 命名空间+id
List<User> list = session.selectList("com.yjxxt.mappers.UserMapper.queryAll");
System.out.println(list);
//5.关闭会话资源
session.close();
}
}
Mybatis配置文件详解
核心配置文件
1.configuration
配置文件的根元素,所有其他的元素都要在这个标签下使用(dtd文件规定)
2.environments default="environment"
用于管理所有环境, 并可以指定默认使用那个环境,通过defualt属性来指定
3.environment
用来配置环境,id属性用于唯一标识当前环境
4.transactionManager type="JDBC"
用户配置事务管理器
type属性
用来指定Mybatis采用何种方式管理事务
JDBC : 表示采用与原生JDBC一致方式管理事务
MANAGED: 表示讲事务管理交给其他容器进行, Spring
5.dataSource type="POOLED"
用于配置数据源, 设置Myabtis是否使用连接池技术,并且配置数据库的四个连接参数
type属性:
POOLED : 表示采用连接池技术
UNPOOLED: 表示每次都会开启和关闭连接, 不采用连接池技术
JNDI : 使用其他容器提供数据源
6.property
用于配置数据库连接参数 (driver,url,username,password)
7.Mappers
用于配置扫描sql映射文件
SQL映射文件(mapper)
MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句
查询语句是 MyBatis 中最常用的元素之一(映射文件配置见代码)
1.mapper
SQL映射文件的根元素
namespace 属性
用于指定命名空间, mydatis通过namespace+id的方式用来定位sql语句,所以必须要指定namespace,通过被配置为权限定路径 包名+xml文件名(不带后缀名)
2.select
用来定义查询语句 update insert delete
id 属性
用阿里唯一表示当前sql语句,在当前的命名空间中唯一,不能重复 , 类型方法名
resultType 属性
用于设定查询返回的结果的数据类型,要写类型的权限定名(包名+类名),如果返回值的是集合类型,要定义集合的泛型类型
三个查询方法
selectList("命名空间.id") 用户查询多条数据情况,返回一个List集合, 没有查到数据返回空集合,不是null
selectOne("命名空间.id") 用于查询单条数据,返回一个数据, 如果没有查到返回null
selectMap("命名空间.id",key的字段名) 用于查询多条记录情况, 返回Map集合, 需要指定那个属性作为key, sql查询结果作为value,指定的字段值作为key, 如果查不到, 返回一个空map集合,不是null
package com.yjxxt.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
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 com.yjxxt.pojo.User;
public class UserTest {
public static void main(String[] args) throws IOException {
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis.xml");
//2.获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//3.获取会话
SqlSession session = factory.openSession();
//4.操作
//1)selectList("namespace.id") 用于查询多条数据,返回一个list集合,如果没有查询到任数据,返回空集合,不是null
List<User> list = session.selectList("com.yjxxt.mappers.UserMapper.queryAll");
System.out.println(list);
//2)selectOne("namespace.id")
User user = session.selectOne("com.yjxxt.mappers.UserMapper.queryById");
System.out.println(user);
//3)selectMap("namespace.id","执行作为key的属性")
Map<Integer,User> map = session.selectMap("com.yjxxt.mappers.UserMapper.queryAll", "id");
System.out.println(map);
//5.关闭session
session.close();
}
}
Log4J日志
Log4J简介
日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录。在apache网站:jakarta.apache.org/log4j 可以免费下载到Log4j最新版本的软件包。
日志级别
分为五个级别:
DEBUG(人为调试信息)、INFO(普通信息)、WARN(警告)、ERROR(错误)和FATAL(系统错误)
这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,分别用来指定这条日志信息的重要程度,明白这一点很重要,Log4j有一个规则:只输出级别不低于设定级别的日志信息,假设Logger级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。
Log4J使用
导包
配置文件
可以使用properties或者xml进行配置,使用一个叫 log4j.properties 的配置文件, 会设定 log4j
的设置信息, 例如日志级别, 日志输出方式, 日志格式等等.
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE
# log4j.rootCategory=INFO, CONSOLE, LOGFILE
# 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=- %m %c %l %d{yyyy-MMdd HH:mm:ss}%n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:/test.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=- %m %l%n
常见的日志输出格式
参数 | 描述 | 实例 |
c | 输出Logger所在的类别(即Logger的名字)。允许使用%c{数字}输出部分的名字(从右边往左边数)。见实例 | %c将输出com.log4j.Log4jTest %c{1}将输出Log4jTest %c{2}将输出log4j.Log4jTest %c{3}将输出com.log4j.Log4jTest |
d | 输出日期。允许使用%d{yyyy-MM-dd HH:mm:ss}格式化日期。%d不仅支持JDKSimpleDateFormat的日期格式,还支持log4j自己的日期格式,例如ABSOLUTE,DATE,ISO8601等 | %d将输出2013-06-06 22:24:25,353 %d{yyyy-MM-dd}将输出2013-06-06 %d{ABSOLUTE}将输出22:24:25,353 %d{DATE}将输出06六月2013 22:24:25,253 |
F | 输出所在的类文件名称 | %F将输出Log4jTest.java |
l | 输出语句所在的行数,包括类名,方法名,文件名,行数等 | %l将输出com.log4j.test.Log4jTest.main(Log4jTest.java:50) |
L | 输出语句所在的行数,只输出数字 | %L将输出 50 |
m | 输出日志 | 输出日志,即log.info(“”),log.debug(“”)参数 |
M | 输出方法名 | %M将输出main |
n | 换行.Windows下输出\r\n,Linux下输出\n | 含行 |
p | 输出日志级别(prioriry) | DEBUG,INFO,ERROR,FITAL等 |
r | 输出从程序启动到输出该日志之间的时间间隔,单位毫秒 | %r将输出300 |
t | 输出当前的线程名称 | %t将输出main,Thread-0,Thread-1等 |
% | %%用来输出百分号 |
Mybatis对Log4J的支持
通过settings标签开启 log4j 的支持
settings用于设置 MyBatis 在运行时的行为方式, 例如:缓存, 延迟加载, 日志等.
<!-- settings标签 -->
<settings>
<!-- 设置MyBatis使用log4j日志支持 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
局部调整日志级别, 定制日志的输出
# 提高整体日志级别
log4j.rootCategory=ERROR, CONSOLE
# 单独设置SQL语句的输出级别为DEBUG级别
# 方法级别
# log4j.logger.com.yjxxt.mapper.UserMapper.selAll=DEBUG
# 类级别
# log4j.logger.com.yjxxt.mapper.UserMapper=DEBUG
# 包级别
log4j.logger.com.yjxxt.mapper=DEBUG
通过properties标签实现软编码
src下定义配置文件db.properties
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:XE
username=SCOTT
password=TIGER
properties标签
mybatis核心配置文件中添加properties标签,指定加载外部的properties文件,注意定义位置
<!-- 加载外部的properties文件 -->
<properties resource="db.properties" />
使用方式
获取properties文件中数据时候,要通过${}的方式获取
<environments default="even">
<environment id="even">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
typeAliases标签
用于给java类型定义别名,方便在配置文件中使用
使用方式
给User类定义别名为u
<typeAliases>
<typeAlias type="com.yjxxt.pojo.User" alias="u"/>
</typeAliases>
省略alias属性, 表示类别名为类名, 大小写不敏感
<typeAliases>
<typeAlias type="com.yjxxt.pojo.User"/> alias属性不写,默认类名,不区分大小写
</typeAliases>
可以通过package标签给整个包下的所有类定义别名,别名为类名
<typeAliases>
<package name="com.yjxxt.pojo"/> <!-- 包下所有的类默认类名 -->
</typeAliases>
<select id="queryAll" resultType="u">
select id,uname,upwd,birthday from t_user
</select>
<select id="queryById" resultType="User">
select id,uname,upwd,birthday from t_user where id=125
</select>
Mybatis的内建别名
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
_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 |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
parameterType入参类型
如果执行的是条件查询,DML,需要在调用方法的时候传递参数,此时, 可以在sql标签中通过parameterType属性指定参数的类型(别名|权限定名). 而在sql语句,通过#{}的方式获取参数
一个参数的查询
例如: 根据id查询用户信息,当参数只有一个,#{}可以任意填写匹配参数
<!-- 根据用户id查询用户信息 -->
<select id="queryById" resultType="string" parameterType="int">
select uname from t_user where id=#{id} <!-- 可以任意填写 -->
</select>
SqlSession session = null;
try {
//获取会话工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
//通过工厂生产会话session对象
session = factory.openSession();
//根据id查询用户名 第二个参数为sql传递的参数
String name=session.selectOne("com.yjxxt.mapper.UserMapper.queryById", 126);
System.out.println(name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
session.close();
}
多个参数查询
多个参数传递时, 由于sqlSession中提供的查询方法,只允许传递一个sql参数, 因此可以对多个参数进行封装,可以对象,集合,数组..
<!-- 入参类型: Javabean -->
<select id="queryUserByNamePwd" parameterType="user" resultType="User">
<!-- 如果参数为对象,可以通过#{对象的属性名}匹配不同的属性值 -->
select <include refid="user_all_field"/> from t_user where uname =#{uname} and upwd = #{upwd}
</select>
/*
* 参数为Javabean User对象
* 根据用户名和密码查询用户信息
*/
User user = new User("zhaoliu","4567");
List list=session.selectList("com.yjxxt.mapper.UserMapper.queryUserByNamePwd", user);
System.out.println(list);
入参类型
parameterType: 基本数据类型(四类八种) 包装类 String Date Javabean Map List 数组 ...
基本数据类型|包装类
<!-- 入参类型: 基本数据类型int|Integer -->
<!-- 根据用户id查询用户信息 -->
<select id="queryById" resultType="string" parameterType="int">
</select>
/* 参数基本数据类型 int->Integer
* 根据id查询用户名 第二个参数为sql传递的参数
*/
String name=session.selectOne("com.yjxxt.mapper.UserMapper.queryById", 126);
System.out.println(name);
String
<!-- 入参类型 : String 根据用户名查询用户信息-->
<select id="queryUserByName" parameterType="String" resultType="User">
select <include refid="user_all_field"/> from t_user where uname = #{0}
</select>
//测试参数类型: String
List<User> list=session.selectList("com.yjxxt.mapper.UserMapper.queryUserByName", "zhangsan");
list.forEach(System.out::println);
JavaBean
<!-- 入参类型: Javabean -->
<select id="queryUserByNamePwd" parameterType="user" resultType="User">
<!-- 如果参数为对象,可以通过#{对象的属性名}匹配不同的属性值 -->
select <include refid="user_all_field"/> from t_user where uname =#{uname} and upwd = #{upwd}
</select>
/*
* 参数为Javabean User对象
* 根据用户名和密码查询用户信息
*/
User user = new User("zhaoliu","4567");
List list=session.selectList("com.yjxxt.mapper.UserMapper.queryUserByNamePwd", user);
System.out.println(list);
Map
<!-- 入参类型: Map 根据多个id,查询用户信息-->
<select id="queryUserByIdMap" parameterType="map" resultType="user">
select <include refid="user_all_field"/> from t_user where id = #{id1} or id=#{id2}
</select>
//入参类型: Map
Map<String,Object> paras = new HashMap();
paras.put("id1", 131);
paras.put("id2", 132);
List<User> list=session.selectList("com.yjxxt.mapper.UserMapper.queryUserByIdMap", paras);
list.forEach(System.out::println);
数组|List
<!-- 参数为数组 根据多个用户名查询用户信息-->
<select id="queryByArray" resultType="User">
select <include refid="user_all_field"/> from t_user where uname in(
<!--
foreach 遍历参数
collection="array" array遍历数组参数 list集合
item : 每次遍历获取的数据值
separator : 分隔符(每次遍历的数据使用分隔符进行连接)
-->
<foreach collection="array" item ="item" separator=",">
#{item}
</foreach>
)
</select>
//Array
String[] arr={"hahaha","zhangsan","lisi"};
List<User> list=session.selectList("com.yjxxt.mapper.UserMapper.queryByArray", arr);
list.forEach(System.out::println);
工具类的封装
package com.yjxxt.utils;
import java.io.IOException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MybatisUtils {
private static SqlSessionFactory factory = null;
static {
try {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.xml"));
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 返回一个会话
*/
public static SqlSession getSession() {
SqlSession session =null;
if(factory!=null) {
//session = factory.openSession(); //默认手动提交事务
session = factory.openSession(true); //自动提交
}
return session;
}
}
事务(Transaction)
事务是数据库操作的最小单位,有着ACID的特性,应该保证一个事务下的多条SQL语句要么都成功,要么都失败.
Mybatis中配置了事务管理器,type属性设置JDBC,表示Mybatis采用和原生JDBC相同的事务管理机制
在Myabatis执行操作的开始,将自动提交功能关闭了,需要我们在执行DML操作时候,手动提交设置|设置自动提交
手动提交:
session.commit(); //事务提交
设置自动提交事务:
public static SqlSession getSession() {
SqlSession session =null;
if(factory!=null) {
//session = factory.openSession(); //默认手动提交事务
session = factory.openSession(true); //自动提交
}
return session;
}
增删改操作
SQL映射文件:
<mapper namespace="com.yjxxt.mappers.UserMapper2">
<!-- 测试增删改 : 结果返回的都是影响函数int -->
<!-- 新增 -->
<insert id="insertUser" parameterType="user">
insert into tb_user values(seq_user_id.nextval,#{username},#{userpwd})
</insert>
<!-- 修改 -->
<update id="updateUser" parameterType="user">
update tb_user set userpwd = #{userpwd} where id =#{id}
</update>
<!-- 删除 -->
<delete id="deleteUser" parameterType="_int">
delete from tb_user where id = #{haha}
</delete>
</mapper>
Java测试类:
/*
* 测试增删改功能
*/
public class UserTest02 {
//删除方法
@Test
public void testDelete() {
SqlSession session = MybatisUtils.getSession();
int rows = session.update("com.yjxxt.mappers.UserMapper2.deleteUser", 100);
if(rows>0) {
System.out.println("SECCESS!!!");
}else {
System.out.println("FAILED!!!");
}
//会话关闭
session.close();
}
//修改方法
@Test
public void testUpdate() {
SqlSession session = MybatisUtils.getSession();
User user = new User();
user.setId(100);
user.setUserpwd(9876);
int rows = session.update("com.yjxxt.mappers.UserMapper2.updateUser", user);
if(rows>0) {
System.out.println("SECCESS!!!");
}else {
System.out.println("FAILED!!!");
}
//会话关闭
session.close();
}
//插入方法
@Test
public void testInsert() {
SqlSession session = MybatisUtils.getSession();
User user = new User();
user.setUsername("zhangsan");
user.setUserpwd(789);
//插入方法
int rows = session.insert("com.yjxxt.mappers.UserMapper2.insertUser", user);
//影响行数判断
if(rows>0) {
System.out.println("SECCESS!!!");
//session.commit(); //事务提交
}else {
System.out.println("FAILED!!!");
//session.rollback();
}
//会话关闭
session.close();
}
}
resultType结果类型
基本数据类型(包装类) String Date JavaBean List Map List-Map
Date类型
<!-- Date : 根据id获取生日 -->
<select id="queryDateById" resultType="date" parameterType="int">
select birthday from t_user where id = #{param1}
</select>
/*
* Date : 根据id返回生日
*/
@Test
public void testDate(){
SqlSession session = MybatisUtils.getSession();
Date date = session.selectOne("com.yjxxt.mapper.UserMapper.queryDateById",129);
System.out.println(new SimpleDateFormat().format(date));
session.close();
}
List类型
<!--List类型 :查询指定名字类型的用户信息 -->
<select id="queryUserByUsernameLike" resultType="user" parameterType="string">
select * from t_user where uname like '%'||#{0}||'%'
</select>
/*
* List : 查询指定名字类型的用户信息
*/
@Test
public void testList(){
SqlSession session = MybatisUtils.getSession();
List<User> list = session.selectList("com.yjxxt.mapper.UserMapper.queryUserByUsernameLike","i");
list.forEach(System.out::println);
session.close();
}
Map类型
<!-- Map: 根据用户名和密码查询用户信息 (只能匹配一个用户信息)-->
<select id="queryUserByNamePwdToMap" parameterType="User" resultType="map">
select * from t_user where uname = #{uname} and upwd = #{upwd}
</select>
/*
* Map : 根据用户名和密码查询用户信息
* 返回值: key 属性名
* value 属性值
*/
@Test
public void testMap(){
SqlSession session = MybatisUtils.getSession();
Map<String,Object> map= session.selectOne("com.yjxxt.mapper.UserMapper.queryUserByNamePwdToMap",new User("yinwei","1314"));
System.out.println(map);
session.close();
}
List-Map类型
<!-- Map: 根据用户名和密码查询用户信息 (只能匹配一个用户信息)-->
<select id="queryUserByNamePwdToMap" parameterType="User" resultType="map">
select * from t_user where uname = #{uname} and upwd = #{upwd}
</select>
/*
* List<Map> : 根据用户名和密码查询用户信息,返回多个值
* 返回值: key 属性名
* value 属性值
*/
@Test
public void testListMap(){
SqlSession session = MybatisUtils.getSession();
List<Map<String,Object>> map= session.selectList("com.yjxxt.mapper.UserMapper.queryUserByNamePwdToMap",new User("zhangsan","1234"));
System.out.println(map);
session.close();
}
接口 绑定方案
Myabtis中,提供了一套接口绑定方案,程序员可以提供一个接口,然后提供一个与接口所对应的mapper.xml文件
Myabaits会自动讲接口与xml文件进行绑定,实际上就是Mybatis互根据接口和对应的xml文件创建一个接口的实现类,换言之,就是可以得到接口实现类的一个对象,方便方法的调用
实现方式
定义接口
/*
* 接口 定义了对用户的操作
*/
public interface UserMapper {
//查询所有用户
public List<User> queryAll();
}
映射文件
注意:
xml文件名要与接口名保持一致
namespace属性值 必须与接口的权限定名
id属性必须与抽象方法名保持一致
返回值类型和参数类型与方法的返回值和参数保持一致
<?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.yjxxt.mapper.UserMapper">
<!-- id 与 方法名一致 -->
<select id="queryAll" resultType="User">
select * from t_user
</select>
</mapper>
在核心配置文件中扫描接口
扫描单个接口,可以使用mapper标签的class属性
<!-- mapper 配置扫描接口 -->
<mappers>
<mapper class="com.yjxxt.mapper.UserMapper"/> <!--配置某个接口 -->
</mappers>
扫描多个接口,可以简化配置,使用package标签,表示扫描对应包下的所有接口
<!-- mapper 配置扫描接口 -->
<mappers>
<package name="com.yjxxt.mapper"/>
</mappers>
使用
在使用时,通过SqlSession的getMapper() 方法,返回接口代理对象,从而调用以实现的抽象方法
/*
* 测试接口绑定
*/
@Test
public void test(){
SqlSession session = MybatisUtils.getSession();
//getMapper()获取接口实现类对象,参数为接口的class对象,通过参数指定接口
UserMapper mapper =session.getMapper(UserMapper.class);
//通过对象调用方法
List<User> list = mapper.queryAll();
System.out.println(list);
session.close();
}
通过接口绑定解决多参数传递问题
方式一
a)接口中定义方法
User selByUP(String username, String password);
b) 映射文件中提供对应的标签. 此时, SQL 语句中获取方式有两种, 通过#{arg+数字}或#{param+数字}的方式.
<select id="selByUP" resultType="user">
select * from t_user where username=#{param1} and password=#{param2}
</select>
方式二
a) 接口中定义方法, 参数中使用@Param 注解设定参数名用于在 SQL 语句中使用.
User selByUP(@Param("username") String username, @Param("password")
String password);
b) 映射文件中提供对应的标签. 此时, SQL 语句中获取方式有两种, 通过#{参数名称}或#{param+数字}的方式.
<select id="selByUP" resultType="user">
select * from t_user where username=#{username} and
password=#{password}
</select>
接口代理开发(CRUD)
//测试接口绑定方案crud
public interface UserMapper {
//查询所有用于信息
public List<User> queryUserByNamePwd(@Param("name") String name,@Param("pwd") int pwd);
//新增用户
public int addUser(String name,int pwd);
//修改用户信息
public int updateUser(User user);
//删除用户
public int deleteUser(int id);
}
<mapper namespace="com.yjxxt.mappers.UserMapper">
<select id="queryUserByNamePwd" resultType="User">
select * from t_user where username=#{name} or userpwd = #{pwd}
</select>
<!-- 新增用户 -->
<insert id="addUser">
INSERT INTO T_USER VALUES(seq_user_id.nextval,#{param1},#{param2})
</insert>
<!-- 修改用户 -->
<update id="updateUser" parameterType="User">
update t_user set username=#{username},userpwd=#{userpwd} where id=#{id}
</update>
<!-- 删除用户 -->
<delete id="deleteUser" parameterType="int">
delete from t_user where id=#{id}
</delete>
</mapper>
/*
* 接口实现crud
*/
public class UserTest {
@Test
public void testDelete() {
SqlSession session = MybatisUtils.getSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
int rows = userMapper.deleteUser(202);
if(rows>0) {
System.out.println("成功");
}else {
System.out.println("失败");
}
session.close();
}
@Test
public void testUpdate() {
SqlSession session = MybatisUtils.getSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
int rows = userMapper.updateUser(new User(201,"张三丰",1111));
if(rows>0) {
System.out.println("成功");
}else {
System.out.println("失败");
}
session.close();
}
@Test
public void testInsert() {
SqlSession session = MybatisUtils.getSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
int rows = userMapper.addUser("林更新", 45678);
if(rows>0) {
System.out.println("成功");
}else {
System.out.println("失败");
}
session.close();
}
@Test
public void testSelect() {
SqlSession session = MybatisUtils.getSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List<User> list = userMapper.queryUserByNamePwd("胡歌", 789);
System.out.println(list);
session.close();
}
}
动态 SQL
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
if
用于进行条件判断, test 属性用于指定判断条件. 为了拼接条件, 在 SQL 语句后强行添加 1=1 的恒成立条件
<select id="sel" resultType="user">
select * from t_user where 1=1
<if test="username != null and username != ''"> and username=#{username} </if>
<if test="password != null and password != ''"> and password=#{password} </if>
</select>
where
用于管理 where 子句. 有如下功能:
如果没有条件, 不会生成 where 关键字
如果有条件, 会自动添加 where 关键字
如果第一个条件中有 and, 去除之
<select id="sel" resultType="user">
select * from t_user
<where>
<if test="username != null and username != ''"> and username=#{username} </if>
<if test="password != null and password != ''"> and password=#{password} </if>
</where>
</select>
choose...when...otherwise
这是一套标签, 功能类似于 switch...case...
<select id="sel" resultType="user">
select * from t_user
<where>
<choose>
<when test="username != null and username != ''">
and username = #{username}
</when>
<when test="password != null and password != ''">
and password = #{password}
</when>
<otherwise> and 1=1 </otherwise>
</choose>
</where>
</select>
set
用于维护 update 语句中的 set 子句. 功能如下:
满足条件时, 会自动添加 set 关键字
会去除 set 子句中多余的逗号
不满足条件时, 不会生成 set 关键字
int updUser(User user);
<update id="updUser" parameterType="user">
update t_user
<set>
id=#{id},<!-- 防止所有条件不成立时的语法错误 -->
<if test="username != null and username != ''">
username=#{username},
</if>
<if test="password != null and password != ''">
password=#{password},
</if>
</set>
where id=#{id}
</update>
trim
用于在前后添加或删除一些内容
prefix, 在前面添加内容
prefixOverrides, 从前面去除内容
suffix, 向后面添加内容
suffixOverrides, 从后面去除内容
<update id="updUser" parameterType="user">
update t_user
<!-- prefix: 前缀, 表示向前面添加内容 prefixOverrides: 从前面删除内容 suffix: 后缀, 表示向后面添加内容 suffixOverrides: 从后面删除内容 -->
<trim prefix="set" prefixOverrides="user" suffix="hahaha"
suffixOverrides=","> username=#{username},
</trim>
where id=#{id}
</update>
bind
用于对数据进行再加工, 用于模糊查询
<select id="sel" resultType="user">
select * from t_user
<where>
<if test="username!=null and username!=''">
<bind name="username" value="'%' + username + '%'" />
and username like #{username}
</if>
</where>
</select>
foreach
用于在 SQL 语句中遍历集合参数, 在 in 查询中使用
collection: 待遍历的集合
open: 设置开始符号
item: 迭代变量
separator: 项目分隔符
close: 设置结束符
List<User> selIn(@Param("list") List<Integer> list);
<select id="selIn" parameterType="list" resultType="user">
select * from t_user where id in
<foreach collection="list" open="(" separator="," close=")"
item="item"> #{item} </foreach>
</select>
sql...include
sql用于提取 SQL 语句, include用于引用 SQL 语句
<sql id="mySql"> id, username, password </sql>
<select id="selIn" parameterType="list" resultType="user">
select
<include refid="mySql" />
from t_user where id in
<foreach collection="list" open="(" separator="," close=")"
item="item"> #{item}
</foreach>
</select>
Mybatis缓存机制
缓存的重要性是不言而喻的。 使用缓存, 我们可以避免频繁的与数据库进行交互, 尤其是在查询越多、缓存命中率越高的情况下, 使用缓存对性能的提高更明显。
mybatis 也提供了对缓存的支持, 分为一级缓存和二级缓存。 但是在默认的情况下, 只开启一级缓存。
一级缓存
默认开启. 线程级别的缓存, SqlSession 的缓存;
在一个 SqlSession 生命周期中有效. SqlSession 关闭,缓存清空;
在同一个 SqlSession 中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据;
不同的 SqlSession 之间的缓存是相互隔离的;
用一个 SqlSession, 可以通过配置使得在查询前清空缓存;flushCache="true"
任何的 UPDATE, INSERT, DELETE 语句都会清空缓存。
二级缓存
进程级别的缓存, SqlSessionFactory 的缓存
在一个SqlSessionFactory生命周期中有效. 可以在多个SqlSession 生命中期中共享.
默认关闭, 需要使用的时候, 要为某个命名空间开启二级缓存(在 mapper.xml 中配置cache).
<!-- 开启二级缓存, 要求实体类进行序列化 -->
<cache />
由于在更新时会刷新缓存, 因此需要注意使用场合:查询频率很高, 更新频率很低时使用, 即经常使用 select, 相对较少使用delete, insert, update。
缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响。但刷新缓存是刷新整个 namespace 的缓存, 也就是你 update 了一个, 则整个缓存都刷新了。
最好在 「只有单表操作」 的表的 namespace 使用缓存, 而且对该表的操作都在这个 namespace 中。 否则可能会出现数据不一致的情况。
二级缓存应用场景:
对于访问多的查询请求并且用户对查询结果实时性要求不高的情况下,可采用mybatis二级缓存,降低数据库访问量,提高访问速度,如电话账单查询
根据需求设置相应的flushInterval:刷新间隔时间,比如三十分钟,24小时等。。
列名和属性名不一致问题
如果查询时使用 resultType 属性, 表示采用 MyBatis 的Auto-Mapping(自动映射)机制, 即相同的列名和属性名会自动匹配. 因此, 当数据库表的列名和类的属性名不一致时, 会导致查不到数据. 解决该问题可以有两种方式:
查询时, 可以通过列别名的方式将列名和属性名保持一致,继续使用自动映射, 从而解决该问题. 但是较为麻烦.
<select id="selAll" resultType="user">
select id id1, username username1, password password2 from t_user
</select>
使用resultMap
resultMap用于自定义映射关系, 可以由程序员自主制定列名和属性名的映射关系. 一旦使用 resultMap, 表示不再采用自动映射机制.
<resultMap type="user" id="umap">
<!-- id用于映射主键 -->
<id column="id" property="id1" />
<!-- 非主键使用result映射 -->
<result column="username" property="username1" />
<result column="password" property="password1" />
</resultMap>
<select id="selAll" resultMap="umap"> select * from t_user </select>
关系映射查询
数据库中表与表之间的关系:
一对一 (人->身份证)
一对多 (夏令营->学生)
多对一 (学生->班级)
多对多 (学生->课程)
resultMap 的关联方式实现多表查询(一对一|多对一)
在 StudentMapper.xml 中定义多表连接查询 SQL 语句, 一次性查到需要的所有数据, 包括对应班级的信息.
通过resultMap标签定义映射关系, 并通过association标签指定对象属性的映射关系. 可以把association标签看成一个resultMap标签使用. javaType 属性表示当前对象, 可以写全限定路径或别名.
学生类中添加一个班级类型的属性 :
public class Student implements Serializable{
private int id;
private String name;
private int age;
private String gender;
private int cid;
private Clazz cls;
}
StudentMapper:
<resultMap type="student" id="smap">
<id property="id" column="sid" />
<result property="name" column="sname" />
<result property="age" column="age" />
<result property="gender" column="gender" />
<result property="cid" column="cid" />
<association property="cls" javaType="clazz">
<id property="id" column="cid" />
<result property="name" column="cname" />
<result property="room" column="room" />
</association>
</resultMap>
<select id="selAll" resultMap="smap">
select s.id sid, s.name sname,
s.age, s.gender, c.id cid, c.name cname, c.room from t_student s left
join t_class c on s.cid=c.id
</select>
resultMap 的关联方式实现多表查询(一对 多)
在 ClazzMapper.xml 中定义多表连接查询 SQL 语句, 一次性查到需要的所有数据, 包括对应学生的信息.
通过resultMap定义映射关系, 并通过collection标签指定集合属性泛型的映射关系. 可以collection标签看成一个resultMap标签使用. ofType 属性表示集合的泛型, 可以写全限定路径或别名.
班级类中添加一个List容器存储学生类型 的数据 :
public class Clazz implements Serializable{
private int id;
private String name;
private int roomNum;
private List<Student> stus;
}
ClazzMapper.xml:
<mapper namespace="com.yjxxt.mapper.ClazzMapper">
<resultMap type="clazz" id="cmap">
<id property="id" column="cid" />
<result property="name" column="cname" />
<result property="room" column="room" />
<collection property="stus" javaType="list"
ofType="student">
<id property="id" column="sid" />
<result property="name" column="sname" />
<result property="age" column="age" />
<result property="gender" column="gender" />
<result property="cid" column="cid" />
</collection>
</resultMap>
<select id="selAll" resultMap="cmap">
select c.id cid, c.name cname,
c.room, s.id sid, s.name sname, s.age, s.gender from t_student s right
join t_class c on s.cid=c.id </select>
</mapper>
注解开发
Mybatis中注解的作用
使用注解一般用于简化配置文件. 但是, 注解有时候也不是很友好(有时候反而更麻烦), 例如动态 SQL等,所以注解和配置文件可以配合使用
MyBatis 中常用的注解
CRUD 注解
@Select: 类似于select标签
@Insert: 类似于insert标签
@Update: 类似于update标签
@Delete: 类似于delete标签
package com.yjxxt.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.yjxxt.pojo.Student;
public interface StudentMapper {
@Select("select * from t_student")
List<Student> selAll();
@Insert("insert into t_student values (default, #{name}, #{age}, #{gender}, #{cid})")
int insStu(Student student);
@Update("update t_student set age=#{1} where id=#{0}")
int updStu(int id, int age);
@Delete("delete from t_student where id=#{0}")
int delStu(int id);
}
其他注解
@Results: 类似于resultMap标签
@Result: 类似<resultMap的子标签
@One: 类似于association标签
@Many: 类似于collection标签
public interface StudentMapper {
@Select("select * from t_student")
@Results(
value = { @Result(column="id", property="id", id=true),
@Result(column="name", property="name"),
@Result(column="age", property="age"),
@Result(column="gender", property="gender"),
@Result(column="cid", property="cid"),
@Result(property="clazz",
one=@One(select="com.yjxxt.mapper.ClazzMapper.selById"),
column="cid")
}
)
List<Student> sel();
}
public interface ClazzMapper {
@Select("select * from t_class where id=#{0}")
Clazz selById(int id);
}
逆向工程
mybatis-generator是一款mybatis自动代码生成工具,可以通过配置,快速生成pojo,mapper和xml文件.
官方网址:http://mybatis.org/generator/configreference/xmlconfig.html
generatorConfig.xml配置
需添加到资源包下 src下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--数据库驱动 -->
<context id="Tables" targetRuntime="MyBatis3">
<!-- 生成的Java文件的编码 -->
<property name="javaFileEncoding" value="UTF-8" />
<!-- JavaBean 实现 序列化 接口 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<commentGenerator>
<property name="suppressDate" value="true" />
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="false" />
</commentGenerator>
<!--数据库链接地址账号密码 -->
<jdbcConnection
driverClass="oracle.jdbc.driver.OracleDriver"
connectionURL="jdbc:oracle:thin:@localhost:1521:xe"
userId="SCOTT"
password="TIGER">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true 时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--生成 Model 类存放位置 -->
<javaModelGenerator targetPackage="com.yjxxt.pojo"
targetProject="mybatis12\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--生成映射文件存放位置 -->
<sqlMapGenerator targetPackage="com.yjxxt.mappers"
targetProject="mybatis12\src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--生成接口类存放位置(Dao|Mapper) -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.yjxxt.mappers" targetProject="mybatis12\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 参考数据库中需要生成的对应表信息 -->
<table tableName="dept" domainObjectName="Dept"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="emp" domainObjectName="Emp"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>