逆水行舟 —— MyBatis
第一轮总结性笔记
MyBatis的基础
根据MyBatis的官方介绍:
整个测试项目结构如下:使用Maven架构项目
pom.xml : 所需依赖如下
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
mybatis-config.xml : mybatis的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "mybatis-3-config.dtd" >
<configuration>
<!-- 可以配置多个运行环境,但是每个SqlSessionFactory 实例只能选择一个运行环境 -->
<environments default="work">
<environment id="work">
<transactionManager type="JDBC"></transactionManager>
<!-- UNPOOLED POOLED JNDI -->
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mytest" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
StudentMapper.xml : 映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<mapper namespace="com.test.mybatis.dao.StudentMapper">
<!--保存一个学员信息-->
<insert id="saveStudent" parameterType="com.test.mybatis.pojo.Student">
insert into student values(default,#{username},#{address},#{age},#{hobby})
</insert> </mapper>
测试代码:
public class MainTest {
public static void main(String[] args) throws IOException {
Student stu = new Student("Tonny","断背山",22,"吃喝嫖赌");
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
sqlSession.insert("saveStudent", stu);
sqlSession.commit();
sqlSession.close();
}
}
运行后,我们向数据库插入了一个学员信息,检测程序运行情况:
MyBatis的执行流程
我们看大概的的执行过程:源码部分真的太难了,我这菜鸟暂时还是看不懂,只能知道这个大概
1.将sql语句和数据库相关的配置保存在配置文件中
2.SqlSessionFactoryBuilder().build(InputStream is),将配置信息读取并封装到 configuration 对象中
并将其作为属性交给SqlSessionFactory接口的实现DefaultSqlSessionFactory;
3.SqlSessionFactory工厂在创建SqlSession对象时提供相关属性:
1> Configuration对象 贯穿MyBatis执行的整个流程
2> dirty :true sql执行成功后 ,可以事务提交
false sql语句发送失败,事务进行回滚
3> Executor执行器对象(真正动手的对象)
创建 Statement对象,在创建过程中依靠MapperStatement对象将赋值内容和sql占位符进行绑定
\4. SqlSession.commit() 根据 dirty的Boolean 值确定是提交还是回滚
\5. SqlSession.close() 断开连接
第二轮总结性笔记
通过MyBatis核心配置文件如何找到映射文件的几种方式
在核心配置文件中有一个标签 <mappers> </mappers>
其中允许存放的类型有如下几种,分别代表了不同的读取策略和响应的规则:
关于上面这只目录结构我想说的是:
这里有坑1:在Resourse创建文件夹的时候,给我创建成了com.test.nybatis.dao 这一个文件夹,导致我半天没找到错误
这里有坑2:如果我们把映射文件放在Java目录中的Dao目录下,在打包时不会打包,需要配置相关插件才行:
TypeHandlers-类型转换器的运用
当我们的实体类和数据库的字段类型不一致时,类型转换器就能起到很好的一个的转换作用,达到数据类型一致
今天我们的列子是 数据库中字段数据类型为 int 实体类中的属性类型为 Boolean
下面我们通过 TypeHandler 对其进行转换
写一个类实现 TypeHandler接口,并重写其中的几个方法,我们对其中两个方法重新实现
/**
* 自定义类型转换器
*/
public class MyTypeHandler implements TypeHandler {
/**
* 这个方法,是在想数据库中插入类型不匹配时会调用的转换方法
*/
@Override
public void setParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {
if (o == null){
preparedStatement.setInt(i, 0);
return;
}
Boolean flag = (Boolean)o;
Integer value = flag?1:0;
preparedStatement.setInt(i, value);
System.out.println("经过了类型转换器");
}
/**
* 这个方法,是在想数据库中查询结果不匹配实体类时会调用的转换方法
*/
@Override
public Object getResult(ResultSet resultSet, String s) throws SQLException {
System.out.println("经过了类型转换器 int 变 Boolean"); int gender = resultSet.getInt(s); //从结果集中获取全部为int的字段
Boolean flag = Boolean.FALSE;
if (gender == 1){ //如果性别为1 则返回 true封装到Student实体中
flag = Boolean.TRUE;
}
return flag;
}
然后再核心配置文件中注册这个类型转换器:
<!--注册自定义类型转换器-->
<typeHandlers>
<typeHandler handler="com.test.mybatis.Handler.MyTypeHandler" javaType="Boolean" jdbcType="NUMERIC" />
</typeHandlers>
在映射文件中指定使用该类型转换器的场合:
<resultMap id="StudentResultMap" type="Student">
<result column="gender" property="gender" typeHandler="com.test.mybatis.Handler.MyTypeHandler"></result>
</resultMap> <!--插叙一个学员信息,resultMap指定了一个的一个结果类型 在该类型中设有类型转换器-->
<select id="findStuById" parameterType="int" resultMap="StudentResultMap">
select * from student where id = #{id}
</select>
然后我们测试一下 是否可以将实体类中的 Boolean类型的数据通过类型转换器转换为 int 类型插入到数据库中
注意查看gender属性和gender字段的值:
类型转换器生效,boolean 转换为 int 插入到数据库
值得注意的事,类型转换器会对整个表都为某个类型的字段做转换,不能指定为单个字段做转换
但是可以在查询的时候为指定某一个字段转换 ResultMap标签
ObjectFactory对象工厂的运用
这个就比较简单了,在MyBatis为我们对象关系映射的时候会创建实体类的对象实列,我们可以操作这个创建过程
下面的列子是数据库中有一个字段在实体类中并没有相应的属性与其对应,但我们可以为其赋值最后一并作为查询接轨返回
/**
* 继承高抽象类 重写create方法,有点代理的意思在里面
* 还是利用父类创建结果封装对象,我们对其增强了
*/
public class MyObjectFactory extends DefaultObjectFactory { @Override
public Object create(Class type) {
com.test.mybatis.pojo.Student stu = null;
if (type == Student.class) {
com.test.mybatis.pojo.Student student = (Student) super.create(type); //利用父类方法创建对象
student.setCountry("China"); //数据库是没有这个字段的,我们手动封装属性
return student; //将代理对象返回
}
return super.create(type);
}
}
其次就是在核心配置文件中注册这个对象工厂类 :
<objectFactory type="com.test.mybatis.Factory.MyObjectFactory" />
然后我们查询看看: 注意看 country属性,在数据库是没有该字段的,我们为其赋值了
Plugins拦截器的使用
plugins的意思为插件,其实就是拦截的意思,所有的插件都是利用代理的方式,通过拦截,实现增强。
/**
* @Intercepts 表示当前类为一个Intercepts实现类,其职位 @Signature数组
* @Signature 通过方法名 接口 参数类型 指定要拦截的方法
*/
@Intercepts({
@Signature(method="query",type = Executor.class,args = {
MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class
})
})
public class MyPlugins implements Interceptor {
/**
* 被拦截时要执行的方法
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
ArrayList list = (ArrayList) invocation.proceed(); //获取拦截对象
Iterator iterator = list.iterator();
if (iterator.hasNext()){
Student stu = (Student) iterator.next();
String old = stu.getAddress();
stu.setAddress("北海道");
System.out.println("经过Plugins拦截器--address的原值:" + old + " " + "增强后的值:" + stu.getAddress());
}
return list;
} /**
* 封装目标对象的方法,可以返回本身或者代理对象
* target : 目标
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
我们写了一个自己的拦截器来覆盖父类的拦截诶起,实现自己的逻辑,将其注册:
<!--注册找一个拦截器 Plugins-->
<plugins>
<plugin interceptor="com.test.mybatis.Plugins.MyPlugins" />
</plugins>
然后测试查询一下:
这里值得注意的是该拦截器只能拦截四中类型的接口:
Executor.class :
MyBatis的执行器,真正干活的接口,SqlSession只是个牌面
StatementHandler.class :
在Executor执行查询的时候,会创建它
它的任务就是和数据库对话。在它这里会使用parameterHandler和ResultHandler对象为我们绑定SQL参数和组装最后的结果返回。
ParameterHandler.class :
对预编译语句进行参数绑定,完成预编译参数的设置
ResultSetHandler.class :一般不用,不做介绍
第三轮总结性笔记
#{}和 ${}的区别及使用场景
在MyBatis中提供了两种方式读取配置参数到 sql 语句中,完成查询,
# { } : 实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?,
通过预编译的方式,可以防止sql注入的安全问题
$ { } : 采用直接赋值的方式注入值,无法防止sql注入,但是也有存在的必要性,
比如指定动态的表名或者动态的指定排序的字段 也需要 $ { }
这里值得注意的是:因为是拼接注入,如果是字符串记得要把值引号引起来,不然报异常
ResultType 和 ResultMap不做讲解
因为太简单了,难的写Demo,ResultMap一般就用于数据库字段和实体类属性不一致时,我们进行手动绑定
动态SQL
加入我们有一个电脑商品实体类,它有几个简单的属性,品牌、价格、显卡级别、处理器级别、屏幕
当一个用户只对他的品牌做了要求,则我们的查询条件只动态包含 : where 品牌 = ?
当用户对品牌和显卡做了要求 : where 品牌 = ? and 显卡 = ?
如何实现这种动态的sql语句的编写呢?动态Sql就是面对的这种情况,下面看代码:
if 的使用:
choose 、 when 、otherwise : 对应比喻为 switch case default
简单的就不演示,一笔带过,稍微复杂的记一下:
where : 可以自动的将第一个条件前面的逻辑运算符(or,and)去掉
set : 可以把拼接sql后的 , 自动屏蔽掉
trim : 刚开始以为是去除字符串前的空格,NO! 他表示替换的意思
上面三个都具有一定的对比性,所以写在一起,下面看看一个比较复杂的
foreach 遍历集合,将得到的值填充到sql语句中
foreach 遍历集合,将得到的值填充到sql语句中
遍历集合中的元素作为查询条件:
当然还可以遍历数组, collection="array",其他无特别变化
之外还可以遍历Map,下面我们一起来看看:
这里注意在mapper中要使用注解才可以在映射文件中,调用对应的方法,获得值,不然会报错,我卡了一会在这儿
级联查询
一对多环境 :collection
在很多时候,我们都会使用到级联查询,今天的列子是班级和学生,一个班级有多个学生,一对多的关系
在班级实体类中除了有班级的信息以外,还有一个 List<Student> 属性,用来表示该班级下多个学生的集合
映射文件如下所示:
关于上面这个映射文件我有点疑惑的是:按理说Student建立映射关系时,字段和属性名相同,应该可以省略绑定的啊,但是好像不行
还有另一种方式可以实现多对一的映射关系: 特别的地方我已经高亮显示了
下面这一种方式,在配置了延迟加载是是具有延迟加载功效的,
property=stuList 表示多的一方的数据封装集合的属性名
ofType=Student 表示集合中的对象实体
select=**** 这里采用了省略的方法,其实应该是全路径接口.方法名这种方式,然后该方法会再调用与之关联的sql标签
比如:select="com.test.dao.ClassDao.findStuByClassId"
column=****的意思就是以那个上方那个属性作为检索条件,必须得和该findStuByClassId的入参一致
一对一环境 : associatio
多对一的关系,其实多个学生对应一个班级,但是我们在查询的时候,和一对一差不多
当然除了使用association以外,我们也可以专门新建一个Vo类用来接受检索的结果集
多对多环境:一对多 + 一张中间表
列子我就不写了,简单口述一下即可,我们有一个用户表,一个角色表,一个用户可能有多个角色,而一个角色可能被多个用户所拥有,
这个时候我们就创建一个用户角色中间表,将多对多的关系拆成两个一对多的关系即可
实现为:在用户类中添加一个List<jiaose>属性 ,在角色类中添加一个List<用户>属性,完成拆分
MyBatis的注解开发
粗糙的CRUD
常用注解说明
@Insert :实现新增
@Update :实现更新
@Delete :实现删除
@Select :实现查询
@Result :实现结果集封装
@Results :可以与@Result 一起使用,封装多个结果集
@ResultMap :实现引用@Results 定义的封装
@One :实现一对一结果集封装
@Many :实现一对多结果集封装
@SelectProvider : 实现动态 SQL 映射
@CacheNamespace :实现注解二级缓存的使用,标注在接口上
public interface IUserDao {
//查询所有
//注解详细说明: @Results代替的是<resultMap>,该注解中可以使用单个@Result也可以使用多个成集合,如下所示
//@Result注解内:id(是否为主键字段)、column(数据库字段名)、property(属性名)
//这里如果涉及到一对一级联查询为 : @Result(column=" ",property=" ",one=@One(select="com.test.dao.IUserDao.findById"))
//这里如果涉及到一对多级联查询为 : @Result(column=" ",property=" ",many=@Many(select="com.test.dao.IAccDao.findByUid"))
//如果想使用延迟加载为(一对一同理): many=@Many(select="com.test.dao.IAccDao.findByUid",fetchType=FetchType.LAZY
//fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday")
})
List<User> findAll();
//根据id插叙用户
@Select("select * from user where id = #{uid} ")
@ResultMap("userMap")
User findById(Integer userId);
//插入一条用户信息,并返回主键
@Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address})")
@SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before =
false, statement = { "select last_insert_id()" })
int saveUser(User user);
//修改用户信息
@Update("update user set
username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id=#{id} ")
int updateUser(User user);
//删除用户信息
@Delete("delete from user where id = #{uid} ")
int deleteUser(Integer userId);
//使用聚合函数
@Select("select count(*) from user ")
int findTotal();
结束语
MyBatis就记到这儿结束了,在日后的工作中肯定还会再次深层次的研究,到时候再补上
逆水行舟 —— MyBatis的更多相关文章
- Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码
在文章:Mybatis源码解析,一步一步从浅入深(一):创建准备工程,中我们为了解析mybatis源码创建了一个mybatis的简单工程(源码已上传github,链接在文章末尾),并实现了一个查询功能 ...
- 【分享】标准springMVC+mybatis项目maven搭建最精简教程
文章由来:公司有个实习同学需要做毕业设计,不会搭建环境,我就代劳了,顺便分享给刚入门的小伙伴,我是自学的JAVA,所以我懂的.... (大图直接观看显示很模糊,请在图片上点击右键然后在新窗口打开看) ...
- Java MyBatis 插入数据库返回主键
最近在搞一个电商系统中由于业务需求,需要在插入一条产品信息后返回产品Id,刚开始遇到一些坑,这里做下笔记,以防今后忘记. 类似下面这段代码一样获取插入后的主键 User user = new User ...
- [原创]mybatis中整合ehcache缓存框架的使用
mybatis整合ehcache缓存框架的使用 mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓 ...
- 【SSM框架】Spring + Springmvc + Mybatis 基本框架搭建集成教程
本文将讲解SSM框架的基本搭建集成,并有一个简单demo案例 说明:1.本文暂未使用maven集成,jar包需要手动导入. 2.本文为基础教程,大神切勿见笑. 3.如果对您学习有帮助,欢迎各种转载,注 ...
- mybatis plugins实现项目【全局】读写分离
在之前的文章中讲述过数据库主从同步和通过注解来为部分方法切换数据源实现读写分离 注解实现读写分离: http://www.cnblogs.com/xiaochangwei/p/4961807.html ...
- MyBatis基础入门--知识点总结
对原生态jdbc程序的问题总结 下面是一个传统的jdbc连接oracle数据库的标准代码: public static void main(String[] args) throws Exceptio ...
- Mybatis XML配置
Mybatis常用带有禁用缓存的XML配置 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ...
- MyBatis源码分析(一)开篇
源码学习的好处不用多说,Mybatis源码量少.逻辑简单,将写个系列文章来学习. SqlSession Mybatis的使用入口位于org.apache.ibatis.session包中的SqlSes ...
随机推荐
- TreeView的三种状态,全选,全不选,半选中
我知道的设置treeview节点的三种状态,如果不是买的控件,那么通过代码,只能设置两种状态,我知道的有三种方法, 第一种是重写treeview,第二种是把三种状态做成小图标,让节点复选框随着不同的状 ...
- nohup python 没有print输出
nohup python -u crake.py >run.log 2>&1 &
- P2146 [NOI2015]软件包管理器
题目链接:https://www.luogu.org/problemnew/show/P2146 题目描述 Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安 ...
- Appium+Python自动化 1 环境搭建(适用windows系统-Android移动端自动化)
一.安装并配置 java jdk ①下载 java jdk后 安装,安装完成后,配置环境变量 打开计算机->系统属性->高级系统设置->环境变量->新建(系统变量),如图所示: ...
- 【翻译】Flume 1.8.0 User Guide(用户指南) Channel
翻译自官网flume1.8用户指南,原文地址:Flume 1.8.0 User Guide 篇幅限制,分为以下5篇: [翻译]Flume 1.8.0 User Guide(用户指南) [翻译]Flum ...
- linux crontab 命令,最小的执行时间是一分钟,如需要在小于一分钟内重复执行
编写shell脚本实现 crontab.sh #!/bin/bash step=2 #间隔的秒数,不能大于60 for (( i = 0; i < 60; i=(i+step) )); do $ ...
- apache学习笔记
httpd -k restart -n apache24 [注意在wamp下名字叫wampapache] http://blog.sina.com.cn/s/blog_692a024c0102vuq ...
- ps最最基础的文档
因为学习PHP,但是公司没有前端工程师,修图的时候只好找被人帮忙,一个简答的问题,其实几分钟就搞定了,还要麻烦别人,就自己学了一下ps.一共花了3天时间.学习了一些简单的操作. 工具:Adobe Ph ...
- Day09 (黑客成长日记) 爬虫入门
爬虫的基本流程: 发起请求通过HTTP库向目标站点发起请求,也就是发送一个Request,请求可以包含额外的header等信息,等待服务器响应 获取响应内容如果服务器能正常响应,会得到一个Respon ...
- python生成exe文件
安装pyinstaller pyinstaller支持python2和python3 命令行安装:pip install pyinstaller pyinstaller --icon=duoguan. ...