深入理解MyBatis

深入理解MyBatis

statement和preparedstatement区别

  1. statement是语句对象,preparedstatement是预编译语句对象。
    前者每次执行都需要安全性检测和编译,后者第一次需要,以后不需要
  2. 前者即便执行的语句结构一样但参数不同,但执行一百次仅有参数不同的sql,数据库就要生成100此执行计划,效率很低。而后者可重用第一次的执行计划
  3. 前者不安全,比如where’ or 1==1 or '的问题。后者可防sql注入,语句中使用了占位符,规定了sql语句的结构。用户可以设置"?"的值,但是不能改变sql语句的结构.
    简单来说,前者如果sql注入,被注入的部分也会作为sql语句的部分进行执行,而后者在执行前sql的结构已经固定,传来的参数只会作为参数,不会作为sql的一部分进行执行,因此结构不会改变
  4. 前者的拼接sql实在恶心,后者无需拼接sql

Mybatis对象关系

多对一

对象关系设计:

  1. 对象关联关系(多对一),因此数据库将department的主键拿到employee中作外键,employee来维护关系
  2. 类的设计的话,department类中有id和name,emploee中除了id,name还有dept(类型为department),因为靠employee来维护关系,因此把department对象拿来作为成员变量

对象关系实现:
保存:

  1. 创建两个emplyee对象和一个department对象,将employee中的dept设置为创建的那个department对象
  2. 首先department是一方,需要先保存,保存sql中需要设置自增主键,保存完就有新生成的主键insert into department(name) values (#{name})
  3. 保存两个employee, 语句insert into employee(name,dept_id) values(#{name},#{dept.id})
  4. 如果先保存employee,需要额外增加两条update语句,不推荐

查询:

  • 方式一:通过发送额外sql(造成n+1问题)
  1. 首先在department和employee中都需要先写好基本的查询语句,department为select id,name from department where id = #{id}
    employee为select id,name,dept_id from employee where id = #{id}
  2. 先查询得出employee对象,在查询employee时需要语句中为dept_id,但对象中是dept,语句对象和实际对象不一致,使用ResultMap
  3. 在resultmap中通过association发送额外sql,将查询的dept_id传入到额外sql中,发送的额外sql为department的mapper中的查询语句,将查询的结果封装到dept中
<association property="dept" column="dept_id"
  	select="cn.wolfcode.many2one.DepartmentMapper.selectByPk">
</association>
  1. 打印employee会发现department的信息也会出现,nice
  • 方式二:通过关联查询
  1. 通过关联查询,仅需要在EmployeeMapper中写一条关联查询
<select id="selectByJoin" resultMap="BaseMap">
	select c.id,c.name,d.id d_id,d.name d_name
	from clerk c join department d on c.dept_id = d.id
	where c.id = #{id}
</select>
  1. 由查询结果看出,c.id和c.name可封装到clerk中,d_id和d_name要封装到employee中的dept中,因此在resultMap中,只需要将结果封装即可
<association property="dept" javaType="cn.wolfcode.many2one.Department">
  	<result column="d_id" property="id"/>
  	<result column="d_name" property="name"/>
</association>

一对多

对象关系设计:
表设计和多对一相同,employee类中仅有id和name即可,department中存放一个employee集合,集合先new出来防止后面空指针

对象关系实现:
保存:
先保存clerk和department都行,因为必须使用更新语句去更新clerk中的dept_id,因为无论怎么保存,现在employee中并没有与department相关的字段,因此更新语句必不可少

查询:

  • 额外sql
    查询部门,然后通过部门获取员工,sql语句是:
<select id="selectByPk" resultMap="BaseMap">
  	select id,name from department where id = #{id}
</select>

此方式和多对一基本无差别,但是注意,多对一仅需要额外sql查询的结果封装到一个字段中,因此需要;在一对多中需要封装到一个集合中因此用

<collection property="clerks" ofType="cn.wolfcode.one2many.Clerk"
			    select="cn.wolfcode.one2many.ClerkMapper.selectByDeptId" column="id"/>
  • 关联查询
    关联查询的sql为:
<select id="selectByPk" resultMap="BaseMap">
    select c.id,c.name,d.id d_id,d.name d_name
    from department d join clerk c
    on d.id = c.dept_id
    where d.id = #{id}
</select>

resulMap的内容变成了:

<collection property="clerks" ofType="cn.wolfcode.one2many.Clerk">
    <result column="id" property="id"/>
    <result column="name" property="name"/>
</collection>

多对多

数据库用中间表维护关系,中间表存放department和employee的主键。对象设计在employee中存放department的集合

保存:
保存完相关信息,记得在写一条sql去保存中间表的信息,此sql就是把deptid和employeeid插入进去即可

查询:
查询通过额外sql,发送的额外sql需要中间表和dept表做内连接

<collection property="teas" ofType="cn.wolfcode.many2many.Teacher"
					select="cn.wolfcode.many2many.TeacherMapper.selectByStuId" column="id"/>

Mybatis四大组件和插件开发

  • Executor
    执行器,调度下边三个对象执行sql并返回结果
  • StatementHandler
    语句处理器,四大对象核心,通过数据库的statement(PreparedmentStatement)执行sql,可直接获得boundsql对象
  • ParameterHandler
    参数处理器,对sql进行参数设置
  • ResultSetHandler
    结果集处理器,将返回的结果集进行封装
    四大组件执行用到了TypeHandler用于类型转换

补充:插件就是在四大对象执行某方法时进行拦截增强,用到了spring的动态代理.
写个类型实现Interceptor接口,并贴上下列标签,然后再config文件中配置即可食用

@Intercepts({@Signature(
		  type= ResultSetHandler.class,
		  method = "handleResultSets",
		  args = {Statement.class})})

注意:
还一个类MappedStatement,里面有SqlSource接口和ResultMap类,sqlsource接口只有一个方法getBoundSql(),返回BoundSql,BoundSql封装sql语句和sql参数
ResultMap封装了结果映射

总结:简单的sql流程分析
MapperProxy是流程的中转站,StatementHandler是四大组件的核心

  1. Mybatis在创建全局的sqlsessionfactory的同时创建了全局的配置对象configuration,这个对象中含盖了配置文件以及所关联mapper文件的所有信息。
  2. factory.openSession()时不仅会创建出sqlsession对象,同时会创建executor对象(默认使用的是其子类simpleexecutor),是调度中心
  3. getMapper()方法获得的代理类MapperProxy,这个类最终是调用executor的方法进行执行sql语句
  4. 在SimpleExecutor执行doUpdate或doQuery方法时,会首先通过全局配置文件configuration创建出StatementHandler对象(StatementHandler有点像jdbc封装的JDBCTemplate,封装了update方法query方法),SimpleExecutor中有两个核心的方法,doUpdate()调用StatementHandler对象中的update方法,doQuery()方法类似,两个方法进一步调用StatementHandler中的方法。
  5. StatementHandler是四大组件核心,封装了jdbc中dql和dml操作,并可以进行参数设置, StatementHandler中除了update和query方法,还有一个getBoundSql()获取boundsql,boundsql中封装了sql语句和sql参数。
  6. 在statementHandler的实现类BaseStatementHandler中会创建ParameterHandler进行参数设置和ResultSetHandler进行结果封装
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值