1.创建两个实体类
CommandContent类和Command类
public class CommandContent {
private String id; //主键
private String content;
private String commandId;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getCommandId() {
return commandId;
}
public void setCommandId(String commandId) {
this.commandId = commandId;
}
}
package com.jh.bean;
import java.util.List;
public class Command {
//主键
private String id;
//指令名称
private String name;
//指令描述
private String descrption;
//指令对应得多条回复
private List<CommandContent> contentlist;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescrption() {
return descrption;
}
public void setDescrption(String descrption) {
this.descrption = descrption;
}
public List<CommandContent> getContentlist() {
return contentlist;
}
public void setContentlist(List<CommandContent> contentlist) {
this.contentlist = contentlist;
}
}
2.在数据库创建两个表,具有关联关系
3.配置文件
//command实体类配置文件
<mapper namespace="Command">
<resultMap type="com.jh.bean.Command" id="Command">
<id column="c_id" jdbcType="VARCHAR" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="descrption" jdbcType="VARCHAR" property="descrption"/>
<collection property="contentlist" resultMap="CommandContent.Content"/>
</resultMap>
<select id="getCommandList" parameterType="com.jh.bean.Command" resultMap="Command">
select c.id c_id,c.name,c.descrption,d.id,d.content,d.commandId
from command c left join commandContent d on c.ID=d.commandId
<where>
<if test="name != null and !"".equals(name.trim())"> and c.name=#{name}</if> <!-- c:if标签里用上一个ognl表达式 xml得双引号为:"" &&在xml里是&& -->
<if test="descrption != null and !"".equals(descrption.trim())"> and a.descrption like '%' #{descrption} '%'</if>
</where>
</select>
<update id="updateMessage" parameterType="com.jh.bean.Command">
update command c left join commandContent d on c.ID=d.commandId set c.name=#{name},c.descrption=#{descrption},d.content=#{content}
where c.id c_id=#{id}
</update>
</mapper>
commandContent配置文件
<mapper namespace="CommandContent">
<resultMap type="com.jh.bean.CommandContent" id="Content">
<id column="id" jdbcType="VARCHAR" property="id"/>
<result column="content" jdbcType="VARCHAR" property="content"/>
<result column="commandId" jdbcType="VARCHAR" property="commandId"/>
</resultMap>
<!--
<insert id="addBacth" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
insert into message(content,commandId) values
<foreach collection="list" item="item" separator=",">
(#{item.content},#{item.commandId})
</foreach>
</insert>
-->
</mapper>
实现拦截的类:
package com.jh.intercept;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import com.jh.entiy.Page;
import com.mysql.jdbc.PreparedStatement;
//type参数表示拦截的部分,method表示拦截的方法,args表示参数
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageIntercept implements Interceptor {
@Override
/*
* 跟踪源码
* 查询是通过excute调度statementHandler来完成的,调度statementHandler是通过prepare方法进行与预编译SQL
* 需拦截的方法是prepare,在此之前完成SQL的重新编写
* 使用mybatis的拦截器进行分页
* 实现intercept接口
* 注意几大对象
*1.statementHandler对象是通过接口里的参数获取.getTarget,它的作用是让数据库的statement(preparedStatement)执行操作
*(此对象是通过routingStatementHandler对象获取)
*2.获取metaobject对象传入的参数为上面的对象
*8.delegate对象的适配器 他是StatementHandler接口对象
*3.MappedStatement对象,是通过metaobject对象的getValue方法,参数为delegate.mappedStatement
*4.Boundsql对象,通过statementHandler对象的getBoundsql方法
*5.mappedStatement对象保存映射的节点(select insert update delete)包括配置的id,缓存信息,resultMap parameterType 等重要配置信息
*7.ParameterHandler用于sql对参数的处理,获取的方法同MappedStatement
*6.boundsql是建立sql语句和参数的对象,有三个常用的属性(sql parameterType parameterOject parameterMappings)
* */
public Object intercept(Invocation invocation) throws Throwable {
//获取StatementHandler对象,通过getTarget方法
StatementHandler statementHandler=(StatementHandler)invocation.getTarget();
//获得metaObject对象
MetaObject metaObject=SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement=(MappedStatement) metaObject.getValue("delegate.mappedStatement");
//获取sql语句的id
String id=mappedStatement.getId();
//用正则表达式选择id
if(id.matches(".+Bypage$")){
//获得原始的sql语句
BoundSql boundSql=statementHandler.getBoundSql();
//通过boundsql获取sql
String sql=boundSql.getSql();
//查询总条数的语句
String sqlCount="select count(*) from ("+sql+")a";
//获取连接,通过invocation参数的getArgs方法
Connection connection=(Connection)invocation.getArgs()[0];
PreparedStatement ps= (PreparedStatement) connection.prepareStatement(sqlCount);
//获取parameterHandler对象
ParameterHandler parameterHandler=(ParameterHandler)metaObject.getValue("delegate.parameterHandler");
//为ps设置参数
parameterHandler.setParameters(ps);
ResultSet rs=ps.executeQuery();
//通过boundsql获取参数
Map<?,?> parameter=(Map<?, ?>)boundSql.getParameterObject();
//获得page参数
Page page=(Page)parameter.get("page");
if(rs.next()){
//设置属性
page.setTotleNumber(rs.getInt(1));
//改造后分页查询的语句,sql语句由boundsql获得,boundsql由delegate获得
String pageSpl=sql+"limit"+page.getDbIndex()+page.getDbNumber();
metaObject.setValue("delegate.boundsql.sql",pageSpl);
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties arg0) {
// TODO Auto-generated method stub
}
}
总结:理解理解java反射机制,理解myBatis执行sql语句的过程,底层代码的实现,映射器的内部组成(三部分)1.MappedStatement 2.SqlSource:提供BoundSql的地方,是上一个的属性 3.Boundsql:它是建立sql的对象,有三个属性(sql,parameterObject,parameterMapping)
在拦截器都需用到这些对象
SqlSession下的四大对象(及拦截器拦截的对象)1
1.Executor代表执行器,其它三大对象都由它来调度 2.statementHandler 3.ParameterHandler 4.ResultHandler:时进行最后数据集的封装返回处理
StatementHandler接口是有RoutingStatementHandler对象实现,而它的delegate对象才是真正服务的statementHandler,真实的statementHandler对象有一个属性Boundsql,而BoundSql里有sql属性,所以最后的获取的sql语句:delegate.BoundSql.Sql
查询的过程是通过Executor调度StatemnetHandler来完成的,调度StatemnetHandler的prepare方法预编译sql,所以我们需要拦截的方法便是prepare
MyBatis的工具类-MethodObject:可以有效的读取或者修改一些重要的对象的参数,其有三个方法普遍使用1.MethodObkect.forObject(Objectobj,ObjectFactoryobjectFactory,ObjectWrapperFactor objectWrapperFactory ),2:Object. getValue(String name)方法用于获取属性的值 支持OGNL 3.void setValue(String name,Object Value)用于修改值 也支持OGNL表达式(PammedStatement,statementHandler都是由MethodObject获取,而MethodObject是由SystemMetaObject.forObject获取)
在完成拦截后要交回给底层代码执行sql语句,invocation.proced()进入责任链下一层
plugin方法:使用默认的MyBatis提供的类生成代理对象