mybatis 应用和原理学习

526 阅读19分钟

JDBC

学习mybatis 之前,先学习jdbc

JDBC(Java Database Connectivity)是 Java 语言中用于连接和操作数据库的 API(应用程序编程接口)。它提供了一套标准的接口,使得 Java 程序能够与各种数据库进行交互,无论是关系型数据库(如 MySQL、PostgreSQL、Oracle 等)还是某些非关系型数据库。

接口规范:SUN(现为Oracle的一部分)提供了一套访问数据库的规范,即JDBC接口,这些接口定义了Java程序与数据库交互的标准方式。 驱动实现:各个数据库厂商会根据SUN的规范提供一套API,用于访问自己公司的数据库服务器。这些API实际上是JDBC接口的实现,被称为JDBC驱动。没有驱动,Java程序无法与数据库建立连接。 工作流程:当Java程序需要与数据库交互时,它会通过JDBC接口调用相应的方法。JDBC接口会将这些方法调用转发给对应的JDBC驱动,由驱动负责具体的数据库操作。 功能如下:

(1) 与数据库建连接。

(2) 向数据库发送 SQL 语句并执行这些语句。

(3) 处理数据返回的结果。


        //加载驱动

        Class.forName("com.mysql.cj.jdbc.Driver");

        //建立连接url(指定连接的路径 语法:“jdbc:mysql://ip地址:端口号/数据库名称”)

        Connection con = DriverManager.getConnection("jdbc:mysql://10.16.158.90:3306/db1", "root", "123456");
        
        // 创建statment
        String updateSql = "SELECT * users SET password = ? WHERE id = ?";
      
        PreparedStatement SelectStmt = conn.prepareStatement(updateSql);

       //执行参数占用
        updateStmt.setString(1, "new_password");
        
        updateStmt.setInt(2, 1);
        
        updateStmt.executeUpdate();

        //执行sql,进行参数替换
            
        ResultSet rs = SelectStmt.executeQuery();

         //处理返回结果

         List<Student> list= new LinkedList<>();

        while (rs.next()){

             Integer sid = rs.getInt("sid");

             String name = rs.getString("name");

             Integer age = rs.getInt("age");

             Date birthday = rs.getDate("birthday");

             Student stu = new Student(sid,name,age,birthday);

             list.add(stu);

        }

        com.close();

        stat.close();

        com.close();

JDBC API主要位于JDK中的java.sql包中(之后扩展的内容位于javax.sql包中),主要包括:

  • Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。

  • DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。

  • Connection:创建数据库连接,创建statement,创建PreparedStatement,以及事务相关提交回滚。

  • Statement:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。

  • PreparedStatement:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。

  • CallableStatement:用以调用数据库中的存储过程。

  • ResultSet:返回值

Mybatis:

图片 1.png

SqlSession:作为MyBatis工作的主要顶层API,核心接口;通过 SqlSession 可以获取 Mapper,调用execute 执行;执行事务。 但是spring 业务代码里不在这里使用事务,这里只是mybatis用的事务。 Connection getConnection(); 获取链接,如果@transactional 已经开启事务,已经获取链接了。

image.png

Executor:MyBatis的内部执行器,它负责创建并调用StatementHandler,操作数据库,事务操作。

image.png

StatementHandler:调用ParameterHandler进行占位符替换,调用statmen执行sql,调用resultsethandler执行对象映射

image.png

prepare--创建statment; parameterize--->调用ParameterHandler;

ParameterHandler:负责将用户传递的参数转换成JDBC Statement所需要的参数。是MyBatis实现SQL入参设置的对象,参数绑定。执行setint,setString操作

ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。是MyBatis把ResultSet集合映射成POJO的接口对象。

TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换。

transaciton:事务处理相关。

image.png

image.png

DataSource:Spring 默认的数据库连接池 HikariDataSource,采用配置,池化一管理connecttion。

Transaction(mybatis事务,含有dataSource)

AbstractRoutingDataSource可以实现动态数据源配置

SqlSource :负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。

BoundSql:动态 SQL 标签解析 → SQL 拼接 → #{} 替换为 ? → 参数映射构建

生成boundSql 过程
 new BaseStatementHandler()
 if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

源码分析

Mybatis 扫描文件注册beanDefiniton过程(扫描的是接口):

@MapperScans->MapperScannerRegistrar->registerBeanDefinitions-->注册MapperScannerConfigurer.class[一顿设置属性]

MapperScannerConfigurer.class -》postProcessBeanDefinitionRegistry->mybatis自己的【ClassPathMapperScanner(重写isCandidateComponent方法)】->先调用父类的sacan扫描xmp文件,注册xmp为ScannedGenericBeanDefinition-->然后调用 org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions->然后把每一个bean中的class设置为MapperFactoryBean;此时每个mapper文件的bean定义类都变成MapperFactoryBean了,然后为每一个beanDefination 设置两个属性(addToConfig=true,sqlSessionFactory);

Mybatis 对上述的beanDefiniton进行实例化: getbean->doGetBean->instantce(MapperFactoryBean){this.mapperInterface = mapperInterface对接口名进行赋值} -->populateBean-->org.mybatis.spring.support.SqlSessionDaoSupport#setSqlSessionFactory-->(setSqlSessionFactory(SqlSessionFactory sqlSessionFactory))-->创建sqlSessionTemplate但是并不是创建sqlsession-->创建bean之后 -->-getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean->factory.getObject()->SqlSessionTemplate.getMapper【创建代理】--->getConfiguration> mapperRegistry.getMapper->mapperProxyFactory.newInstance->Proxy.newProxyInstance-->MapperProxy搞一个代理类;

 public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

mapperProxy:包含以下两个属性
private final SqlSession sqlSession;
private final Class<T> mapperInterface;

//解析出来的数据
SqlSession:
private final SqlSessionFactory sqlSessionFactory;

Mybaits 扫描xml文件过程

当创建第一个mapper Bean的时候->applyPropertyValues(有两个属性MapperFactoryBean,addToConfig)->发现SqlSessionFactory为空,创建SqlSessionFactory SqlSessionFactory——>SqlSessionFactoryBean.getObject()【bean.getObject】—> afterPropertiesSet--》buildSqlSessionFactory; xmlMapperBuilder.parse---》configurationElement解析sql----》放入到Configuration-放入到---》SqlSessionFactory交给bean容器进行管理。;

configuration 是重点包含以下属性:
Environment :(transactionFactory【创建事务】,DataSource)
protected final Map<String, MappedStatement> mappedStatements 

Mybaits 执行流程:

@Autowired 每一个dao层的mapper都是一个mapperProxy

MapperProxy.invoke—》MapperMethodInvoker—-->PlainMethodInvoker-》MapperMethod.execute(convertArgsToSqlCommandParam获取参数) ——>sqlSession.update——>SqlSessionInterceptor.invoke—>getSqlSession(获取sqlsession)【如果开启事务,那sqlsession 已经打开了这个地方获取就行】--->sessionFactory.openSession(创建sqlsession)—>openSessionFromDataSource【创建事务,newExecutor】——>>new sqlsession--->DefaultSqlSession#selectList—>org.apache.ibatis.session.Configuration#getMappedStatement(java.lang.String)-->executor.query——>拦截Executor.query执行拦截器逻辑——>CachingExecutor-->先从缓存里查询数据--》doQuery(baseExecutor)【newStatementHandler[StatementHandler],prepareStatement【(getConnection)trasnction.getconnectuon获取jdbc链接,BaseStatementHandler#prepare(instantiateStatement实例化)【执行拦截器】——>parameterize[参数替换]——> PreparedStatementHandler.真正操作数据库(操作数据库)[StatementHandler];

四大对象拦截器

(1)写一个实现org.apache.ibatis.plugin.Interceptor接口的拦截器类,并实现其中的方法。 (2)添加@Intercepts注解,写上需要拦截的对象和方法,以及方法参数。 (3)Spring项目注意添加@Component注解即可,使其成为Spring管理的一个Bean。

mybatis 默认只拦截Executor、StatementHandler、ParameterHandler和ResultSetHandler。对于我们的自定义拦截器必须使用MyBatis提供的@Intercepts注解来指明我们要拦截的是四种类型中的哪一种接口。

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
    @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})

@Intercepts 标志该类是一个拦截器; @Signature 指明该拦截器具体需要拦截的哪个对象哪个方法;

type 四种类型接口中的某一个接口,如Executor.class。 method 对应接口中的某一个方法名,比如Executor的query方法。 args 对应接口中的某一个方法的参数,比如Executor中query方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法。

插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Plugin.wrap(target, this);,可以在这个方法中提前进行拦截对象类型判断,提高性能:

 public Object plugin(Object target) {
        //只对要拦截的对象生成代理
        if(target instanceof StatementHandler){
            //调用插件
            return Plugin.wrap(target, this);
        }
        return target;
    }

MyBatis拦截器用到责任链模式+动态代理+反射机制;

所有可能被拦截的处理类都会生成一个代理类,如果有N个拦截器,就会有N个代理,层层生成动态代理是比较耗性能的。而且虽然能指定插件拦截的位置,但这个是在执行方法时利用反射动态判断的,初始化的时候就是简单的把拦截器插入到了所有可以拦截的地方。所以尽量不要编写不必要的拦截器。

另外我们可以在调用插件的地方添加判断,只要是当前拦截器拦截的对象才进行调用,否则直接返回目标对象本身,这样可以减少反射判断的次数,提高性能。

执行拦截器的顺序: executor->ParameterHandler--》->StatementHandler-》->ResultSetHandler

  • StatementHandler 拦截器拦截的方法不同,可能执行顺序不同。拦截prepare 执行顺序在ParameterHandler之前,拦截update 在ParameterHandler 之后

Mybatis 分页

1、RowBounds实现分页,mybatis提供了一个简单的逻辑分页使用类RowBounds,在DefaultSqlSession提供的 某些查询接口中我们可以看到RowBounds是作为参数用来进行分页的。 public List selectList(String statement, Object parameter, RowBounds rowBounds)

原理: 在DefaultResultSetHandler中,逻辑分页会将所有的结果都查询到,然后通过while循环来处理RowBounds的offset和limit值来获取最后的结果以达到分页的目的;所以在数据量大的sql中并不适用,它更适合在返回数据结果较少的查询中使用;

2、借助于sql语句进行分页,sql语句后面添加limit分页语句(page—helper)

PageHelper向Mybatis注册PageInterceptor分页拦截器(executor)[拦截的是execute.class]通过 PageHelper.startPage() 方法把分页相关的参数放到 ThreadLcoal 中 Mybatis 执行 SQL 过程中会调用拦截器根据查询 SQL 构建 count SQL从 ThreadLcoal 拿出分页信息,在查询 SQL 后面拼接 limit ?, ?,执行sql finially清空 ThreadLcoal。

Mybatis的优点:

(什么是mybatis) (1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。

(2)MyBatis 可以 XML 或注解来配置映射,将 java对象映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数。

解除sql与程序代码的耦合,便于统一管理; 支持动态参数; 很好的与各种数据库兼容; 能够与Spring很好的集成;

Mybatis的缺点:

(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。 (2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

什么是sql注入:

所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。

eg:

strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
//恶意写入
userName = "1' OR '1'='1";
passWord = "1' OR '1'='1";
//原来的sql变成,恒等成立
strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"

常见的 (1)select * from user_table where username like '%#{username}%' 会报错,改为 select * from user_table where username like '%${username}%'sql注入 正确的写法: select * from user_table where username like concat('%',#{username},'%')

(2)传人ads,dsp;导致查不出数据 select * from user_table where username in (#{usernames}) 改为,会sql注入 select * from user_table where username in (${usernames}) 换成foreach select * from user_table where username in #{username}

(3)不能动态拼接,因为会换成‘’ select * from user_table order by #{column} limit 0,1 于是改成 select * from user_table order by ${column} limit 0,1

#{}和${}的区别是什么?

(1)${}是字符串替换,#{}是预处理;使用#{}可以有效的防止SQL注入,提高系统安全性。

(2)${}是参数直接注入,大括号内不能写jdbc类型,所以可能会引起sql的注入,#{}大括号可以写JDBc类型,防止sql注入提高了系统安全性。

(3)Mybatis在处理时,就是把{}时,就是把{}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;

什么是MyBatis的接口绑定?

MyBatis接口绑定指的是MyBatis允许开发者仅仅通过一个接口而不需要编写实现类,即可完成对数据库操作的映射。接口绑定有两种实现方式。

一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;

另外一种就是通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。

当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

1、当列名和封装查询结果的类的属性名一一对应时:这时MyBatis 有自动映射功能,将查询的记录封装到resultType 指定的类的对象中去

 <mapper namespace="com.hanT.dao.UserDao">
    <!--id对应接口中的方法名,resultType为返回结果的类型,要用类的全限定名-->
    <select id="getUserList" resultType="com.hanT.pojo.User">
        select * from mybatis.user
    </select>
</mapper>

2、当列名和封装查询结果的类的属性名不对应时:
第一种是使用sql列的别名功能,将列的别名书写为对象属性名。[不常用]

     <mapper namespace="com.hanT.dao.UserDao">
    <!--id对应接口中的方法名,resultType为返回结果的类型,要用类的全限定名-->
    <!--使用别名的功能,比如表里叫name,实体里叫tname-->
    <select id="getUserList" resultType="com.hanT.pojo.User">
        select name as tname from mybatis.user
    </select>
</mapper>

第二种是使用标签,逐一定义数据库列名和对象属性名之间的映射关系。[最常用]

有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

Mapper 编写有几种方式

1、接口实现类继承 SqlSessionDaoSupport:使用此种方法需要编写mapper 接口,mapper 接口实现类、mapper.xml 文件

(1)在sqlMapConfig.xml中配置mapper.xml的位置:

<mappers>
        <mapper resource="mapper.xml 文件的地址" />
        <mapper resource="mapper.xml 文件的地址" />
</mappers>

(2)定义mapper接口:

(3)实现类集成SqlSessionDaoSupport:mapper方法中可以this.getSqlSession()进行数据增删改查。

(4)spring 配置:

<bean id="对象ID" class="mapper 接口的实现">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>

2、使用 MapperFactoryBean

(1)在sqlMapConfig.xml中配置mapper.xml的位置,如果mapper.xml和mappre接口的名称相同且在同一个目录,这里可以不用配置

<mappers>
        <mapper resource="mapper.xml 文件的地址" />
        <mapper resource="mapper.xml 文件的地址" />
</mappers>

(2)定义mapper接口:

① mapper.xml中的namespace为mapper接口的地址

② mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致

③ Spring中定义:

<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="mapper 接口地址" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

3、使用 mapper 扫描器

(1)mapper.xml文件编写:

mapper.xml中的namespace为mapper接口的地址;

mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致;

如果将mapper.xml和mapper接口的名称保持一致则不用在sqlMapConfig.xml中进行配置。 

(2)定义mapper接口:

注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录

(3)配置mapper扫描器:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="mapper接口包地址" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>

(4)使用扫描器后从spring容器中获取mapper的实现对象。

在mapper中如何传递多个参数?

1)第一种:
//DAO层的函数
Public UserselectUser(String name,String area);  
//对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
//这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
    
<select id="selectUser"resultMap="BaseResultMap">  
    select *  fromuser_user_t   whereuser_name = #{0} anduser_area=#{1}  
</select>  
    
 
(2)第二种: 使用 @param 注解:
public interface usermapper {
   user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}
然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
<select id=”selectuser” resulttype=”user”>
         select id, username, hashedpassword
         from some_table
         where username = #{username}
         and hashedpassword = #{hashedpassword}
</select>
    
 
(3)第三种:多个参数封装成map
try{
//`#{}`里面的名称对应的是 `Map`里面的key名称。
//这种方法适合传递多个参数,且参数易变能灵活传递的情况。    
public User selectUser(Map<String, Object> params);
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>

    
(4)第四种:使用java bean进行传参
    //`#{}`里面的名称对应的是 `User`类里面的成员属性。
    //这种方法很直观,但需要建一个实体类,扩展不容易,需要加属性,看情况使用。
    public User selectUser(User params);
<select id="selectUser" parameterType="com.test.User" resultMap="UserResultMap">
    select * from user
    where user_name = #{userName} and dept_id = #{deptId}
</select>
    

为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?(关联对象,对象关系)

Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具

Mybatis的一级、二级缓存(在分布式场景中基本不用):

参考文章:tech.meituan.com/2018/01/19/…

一级缓存:

一、MyBatis一级缓存的生命周期和SqlSession一致。缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺
二、一级缓存的问题: MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。

一级缓存是SqlSession级别的缓存,在创建SqlSession的时候会创建exectutor,其父类Base executor里有两个局部变量localCache,localOutputParameterCache即是缓存。在sqlsession 执行查询selectlist的时候会先调用baseExecutor的query。

image.png 然后根据将MappedStatement的Id、SQL的offset、SQL的limit、SQL本身以及SQL中的参数传入了CacheKey这个类,最终构成CacheKey list = resultHandler == null ? (List) localCache.getObject(key) : null;
所以先从缓存里取,然后取到不到在查数据库 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

另外在query方法执行的最后,会判断一级缓存级别是否是STATEMENT级别,如果是的话,就清空缓存,这也就是STATEMENT级别的一级缓存无法共享localCache的原因。

if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } SqlSession的insert方法和delete方法,都会统一走update的流程
在执行update的时候,统一走一下

image.png 清理一下缓存。

二级缓存

一、MyBatis的二级缓存实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。 二、由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。

二级缓存在一级缓存处理前,用CachingExecutor装饰了BaseExecutor的子类,在委托具体职责给delegate之前,实现了二级缓存的查询和写入功能。 image.png 缓存从 ms 里取的 ,所以它不是 sqlsession 级别的,而是namespace级别的

image.png 在CachingExecutor里有个TransactionalCacheManager TransactionalCache实现了Cache接口,CachingExecutor会默认使用他包装初始生成的Cache,作用是如果事务提交,对缓存的操作才会生效,如果事务回滚或者不提交事务,则不对缓存产生影响。

Mybatis动态sql有什么用?执行原理?有哪些动态sql?

Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。 Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签? 答:、、、、,加上动态sql的9个标签,其中为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签

MyBatis如何获取自动生成的(主)键值?

insert 方法总是返回一个int值 ,这个值代表的是插入的行数 如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。 usergeneratedkeys="true" keyproperty="id

参考文章: blog.csdn.net/qq_39368007…

Mybatis 一对一,多对多配置:

select * from class c,teacher t where c.teacher_id=t.t_id and c.c_id=#{id}
<resultMap type="com.lcb.user.Classes" id="ClassesResultMap">  
    <!-- 实体类的字段名和数据表的字段名映射 -->  
    <id property="id" column="c_id"/>  
    <result property="name" column="c_name"/>  
    <association property="teacher" javaType="com.lcb.user.Teacher">  
        <id property="id" column="t_id"/>  
        <result property="name" column="t_name"/>  
    </association>  
</resultMap>  


<!--collection  一对多关联查询 -->  
<select id="getClass2" parameterType="int" resultMap="ClassesResultMap2">  
    select * from class c,teacher t,student s where c.teacher_id=t.t_id and c.c_id=s.class_id and c.c_id=#{id}  
</select>  

<resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">  
    <id property="id" column="c_id"/>  
    <result property="name" column="c_name"/>  
    <association property="teacher" javaType="com.lcb.user.Teacher">  
        <id property="id" column="t_id"/>  
        <result property="name" column="t_name"/>  
    </association>  

    <collection property="student" ofType="com.lcb.user.Student">  
        <id property="id" column="s_id"/>  
        <result property="name" column="s_name"/>  
    </collection>  
</resultMap>  

MyBatis实现一对一有几种方式?具体怎么操作的?

有联合查询和嵌套查询 联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的结果的外键id,去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。

MyBatis实现一对多有几种方式,怎么操作的?

有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在resultMap里面的collection节点配置一对多的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的 结果的外键id,去再另外一个表里面查询数据,也是通过配置collection,但另外一个表的查询通过select节点配置。

Mybatis 延迟加载:

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false 它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

Dao工作原理

通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么? 不错,整体执行流程了。 blog.csdn.net/a745233700/…

请问Dao接口里的方法,参数不同时,方法能重载吗? 不能被重载,key不能重复

不同XML id可以相同吗,为什么? 可以相同,因为namespace不同

 

参考文章:

blog.csdn.net/aasd23/arti…

blog.csdn.net/u011709538/…

blog.csdn.net/a745233700/…