Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理
0
时,就是把
0时,就是把
0时,就是把{}替换成变量的值。
有了#{}为什么还需要${}?
#{}会被预编译处理,可以有效的防止SQL注入,提高系统安全性。
对于需要动态构建SQL的场景 , 比如条件查询,${}可以用来拼接SQL片段 , 构建动态的表名、列名等。
mybatis和hibernate都同处于持久层的框架。
mybatis是粗粒度的封装
hibernate是细粒度的封装
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBG,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程,直接编写原生态sql
Hibernate对象/关系映射能力强,让使用者直接面向对象编程,不用关心sql
Mybatis提供了9种动态sql标签: where | set foreach | if | choose| when | otherwise|trim
分页插件原理:使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sqI,然后重写sql,添加对应的物理分页语句和物理分页参数
在MyBatis中插件是通过拦截器来实现的,那么既然是通过拦截器来实现的,就会有一个问题,哪些对象才允许被拦截呢?
四大对象:Executor , StatementHandler,ParameterHandler,ResultSetHandler
Mybatis只能针对上面的四大对象进行拦截
上面4个创建好之后并没有直接返回,而是都调用executor = interceptorChain.pluginAll(executor);
将上面4个对象都放到pluginAll 做了一个处理,又返回了一个该对象
里面用了对原对象进行的动态代理,代理的时候,加入了拦截器的执行
但是并不是这四大对象中的所有方法都能被拦截,下面就是官网提供的可拦截的对象和方法汇总:

建造者: SqlSessionFactoryBuiler、XMLConfigBuiler
工厂: SqlSessionFactor
**代理:**MappedProxy使用的是jdk的动态代理
模板方法: BaseExcutor和SimpleExecutor
方式一:useGeneratedKeys
<insert id="adduser" parameterType="com.gxa.entity.user"
useGeneratedKeys=true" keyProperty="id"">
INSERT INTO t_user(user_name , pwd)
VALUES(#{username] ,#{pwd})
</insert>
方式二:selectKey标签实现主键返回
<insert id="adduser2" parameterType="com. gxa.entity.user" >
<!-- selectKey标签实现主键返回 -->
<!-- keyproperty:主键对应的pojo中的哪一个属性
<!-- order(设置在执行insert语句前执行查询id的sq1,还是在执行insert语句之后执行查询id的sq1 -->
<!-- resultTypes设置返回的id的类型-->
<selectKey keyProperty="id" order="AFTER" resu1tType="int">
SELECT LAST_INSERT_ID(
</selectKey>
INSER INTo t_user(user_name , pwd) VALUES(#{username},#{pwd})
</inse
1)一级缓存是SqlSession级别的缓存,底层使用map集合实现,当Session flush或close之后,该Session中的所有Cache就将清空,默认打开。
2)二级缓存是SqlSessionFactory级别的缓存,不同的SqlSession可以共享。默认不打开,要开启使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),在它的映射文件中配置;
**3)**对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear。
查询的顺序是:
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之前,一级缓存中的数据会写入二级缓存
(1)读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。
(2)加载映射文件。就是SQL映射文件,文件中配置了操作数据库的SQL语句,需要在mybatis-config.xml中加载,可以加载多个映射文件,每个文件对应数据库中的一张表。
(3)构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory.
(4)创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
(5) Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqISession传递的参数动态地生成需要执行的SQL语句,同时维护查询缓存。
(6) MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
(7)输入参数映射。JDBC对preparedStatement对象设置参数的过程。
(8)输出结果映射。JDBC对结果集的解析过程。
都可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。
1、根据配置文件(全局,sql映射)初始化出Configuration对象
2、创建1DefaultSqlSession对象,里面包含Configuration以及Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
3、DefaultSqlSession.getMapper ():拿到Mapper接口对应的MapperProxy;
4、MapperProxy里面有(DefaultSqlSessioh) ;
5、执行增删改查方法:
1)、调用DefaultSqlSession的增删改查(Executor) ;
2)、会创建StatementHandler对象。同时创建出ParameterHandler和ResultSetHandler
3)、调用StatementHandler预编译参数以及设置参数值;使用ParameterHandler来给sql设置参数
4)、调用StatementHandler的增删改查方法;
5)、ResultSetHandler封装结果
Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:
com,mybatis3.mappers.StudentDao.findStudentByld,可以唯一找到namespace为
com.mybati3.mappers.StudentDao下面id = findStudentByld的MappedStatement。在Mybatis中,每一个标签,都会被解析为一个MappedStatement对象。Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
方式一:foreach
方式二:批量插入,在java代码中打开sqlSession的时候指定: ExecutorType.BATCH
经过测试,foreach的性能很好,甚至比batch好
一对一: association
一对多:collection
Mybatis仅支持association(一对一)关联对象和collection(一对多)关联集合对象的延迟加载,在Mybatis配置文件中置是否启用延迟加载
lazyLoadingEnabled=true | false,默认是关闭的,使用的时候再发送sql去进行查询。
它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完a.getB().getName()方法的调用。这就是延迟加载的基本原理。
当然了,不止是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。