`
zh_harry
  • 浏览: 102873 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
877aca81-daac-33c8-8bf9-3a886cebc6c3
自己动手写java 框架
浏览量:28585
社区版块
存档分类
最新评论

疯子在思考之-mybatis3的sqlSession

    博客分类:
  • JAVA
阅读更多
关于sqlsession我们先看这些问题:
我们做web开发,很容易联系到http的session,那么它跟进http的session有关系吗?
如果非要映射到web开发中的某个对象他到底跟哪个对象很象呢?

sqlssion是用来做什么的?
如果只是数据库连接为什么不直接用sqlconnection呢?
我们从配置文件可以看出他由sqlsessionfactory产生,sqlsessionfactory需要datasource.那么datasouce是什么呢?
它跟数据库连接池有什么关系呢?
sqlsession是线程安全的吗?

最后一个问题很有代表性,关于线程安全问题接下来会专门写一篇文章跟大家分享。

2.1.5.3 SqlSession
Each thread should have its own instance of SqlSession. Instances of SqlSession are not to be shared
and are not thread safe. Therefore the best scope is request or method scope. Never keep references
to a SqlSession instance in a static field or even an instance field of a class. Never keep references
to a SqlSession in any sort of managed scope, such as HttpSession of of the Servlet framework. If
you're using a web framework of any sort, consider the SqlSession to follow a similar scope to that
of an HTTP request. In other words, upon receiving an HTTP request, you can open a SqlSession,
then upon returning the response, you can close it. Closing the session is very important. You should
always ensure that it's closed within a finally block. The following is the standard pattern for ensuring
that SqlSessions are closed:
这是mybatis 官方文档的解释
这里说sqlssion是线程不安全的,它不应该被定成static变量或者类的成员变量,因此最好定义成request域或方法变量(局部变量)
Never keep references
to a SqlSession instance in a static field or even an instance field of a class

不要象servlet框架的httpsession那样 保持对象引用。
Never keep references
to a SqlSession in any sort of managed scope, such as HttpSession of of the Servlet framework


如果我们用象servlet这样的web框架,那么它象httprequest.(这里明确说明了,sqlsession跟request对象很象),后边又强调,即在接收到request请求时打sqlsession.在用response返回时一定要关闭sqlssion
这里明确说明关闭很重要,而且用finally{}块(finally一般用于资源释放

这里已经回答了sqlssion是什么?
sqlsession是用来做什么的?
其实我认为他是用来管理事务的,(实际上也应该是这样)
因为每次请求相当启动一个数据库事务,本次请求可能连接N(N>=0)次数据库,如果我们每次数据库连接都要从重新获取(或者都要从池中获取)这是一种资源浪费,而且从事务角度来考虑也是不对的,这里是threadlocal典型的应用场景。
这里还有一个问题datasource和数据库连接池有什么区别和联系?
数据库连接池是datasource的一种实现

这是jdk package javax.sql.dataSource的注释
/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.sql;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Wrapper;

/** 
 * <p>A factory for connections to the physical data source that this
 * <code>DataSource</code> object represents.  An alternative to the
 * <code>DriverManager</code> facility, a <code>DataSource</code> object
 * is the preferred means of getting a connection. An object that implements
 * the <code>DataSource</code> interface will typically be
 * registered with a naming service based on the 
 * Java<sup><font size=-2>TM</font></sup> Naming and Directory (JNDI) API.
 * <P>
 * The <code>DataSource</code> interface is implemented by a driver vendor.
 * There are three types of implementations:
 * [list=1]
 *   <LI>Basic implementation -- produces a standard <code>Connection</code> 
 *       object
 *   <LI>Connection pooling implementation -- produces a <code>Connection</code>
 *       object that will automatically participate in connection pooling.  This
 *       implementation works with a middle-tier connection pooling manager.
 *   <LI>Distributed transaction implementation -- produces a
 *       <code>Connection</code> object that may be used for distributed
 *       transactions and almost always participates in connection pooling. 
 *       This implementation works with a middle-tier 
 *       transaction manager and almost always with a connection 
 *       pooling manager.
 * [/list]
 * <P>
 * A <code>DataSource</code> object has properties that can be modified
 * when necessary.  For example, if the data source is moved to a different
 * server, the property for the server can be changed.  The benefit is that
 * because the data source's properties can be changed, any code accessing
 * that data source does not need to be changed.
 * <P>
 * A driver that is accessed via a <code>DataSource</code> object does not 
 * register itself with the <code>DriverManager</code>.  Rather, a
 * <code>DataSource</code> object is retrieved though a lookup operation
 * and then used to create a <code>Connection</code> object.  With a basic
 * implementation, the connection obtained through a <code>DataSource</code>
 * object is identical to a connection obtained through the
 * <code>DriverManager</code> facility.
 *
 * @since 1.4
 */

public interface DataSource  extends CommonDataSource,Wrapper {

  /**
   * <p>Attempts to establish a connection with the data source that
   * this <code>DataSource</code> object represents.
   *
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   */
  Connection getConnection() throws SQLException;
      
  /**
   * <p>Attempts to establish a connection with the data source that
   * this <code>DataSource</code> object represents.
   *
   * @param username the database user on whose behalf the connection is 
   *  being made
   * @param password the user's password
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   * @since 1.4
   */
  Connection getConnection(String username, String password) 
    throws SQLException;

}



说得很明白,其实数据库连接池就是datasource的一种实现,这个接口只提供了getconnection方法?那么我们怎么关闭呢?

这篇文章有介绍

http://lizhizhang.iteye.com/blog/1904058

最后一个问题它是线程安全的吗?
sqlsession本身只是一个接口,所以是否线程安全不能从接口上下定论,因为mybatis对sqlsession的实现


前两个是mybatis的实现都是线程不安全的
最后一个是mybatis-spring中间件的线程安全实现
/*
 *    Copyright 2010-2013 The MyBatis Team
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.spring;

import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
import static org.mybatis.spring.SqlSessionUtils.getSqlSession;
import static org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;
import static org.springframework.util.Assert.notNull;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.dao.support.PersistenceExceptionTranslator;

/**
 * Thread safe, Spring managed, {@code SqlSession} that works with Spring
 * transaction management to ensure that that the actual SqlSession used is the
 * one associated with the current Spring transaction. In addition, it manages
 * the session life-cycle, including closing, committing or rolling back the
 * session as necessary based on the Spring transaction configuration.
 * <p>
 * The template needs a SqlSessionFactory to create SqlSessions, passed as a
 * constructor argument. It also can be constructed indicating the executor type
 * to be used, if not, the default executor type, defined in the session factory
 * will be used.
 * <p>
 * This template converts MyBatis PersistenceExceptions into unchecked
 * DataAccessExceptions, using, by default, a {@code MyBatisExceptionTranslator}.
 * <p>
 * Because SqlSessionTemplate is thread safe, a single instance can be shared
 * by all DAOs; there should also be a small memory savings by doing this. This
 * pattern can be used in Spring configuration files as follows:
 *
 * 
 * {@code
 * <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
 *   <constructor-arg ref="sqlSessionFactory" />
 * </bean>
 * }
 * 

*
* @see SqlSessionFactory
* @see MyBatisExceptionTranslator
* @version $Id$
*/
public class SqlSessionTemplate implements SqlSession {

  private final SqlSessionFactory sqlSessionFactory;

  private final ExecutorType executorType;

  private final SqlSession sqlSessionProxy;

  private final PersistenceExceptionTranslator exceptionTranslator;

  /**
   * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory}
   * provided as an argument.
   *
   * @param sqlSessionFactory
   */
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
  }

  /**
   * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory}
   * provided as an argument and the given {@code ExecutorType}
   * {@code ExecutorType} cannot be changed once the {@code SqlSessionTemplate}
   * is constructed.
   *
   * @param sqlSessionFactory
   * @param executorType
   */
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
    this(sqlSessionFactory, executorType,
        new MyBatisExceptionTranslator(
            sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
  }

  /**
   * Constructs a Spring managed {@code SqlSession} with the given
   * {@code SqlSessionFactory} and {@code ExecutorType}.
   * A custom {@code SQLExceptionTranslator} can be provided as an
   * argument so any {@code PersistenceException} thrown by MyBatis
   * can be custom translated to a {@code RuntimeException}
   * The {@code SQLExceptionTranslator} can also be null and thus no
   * exception translation will be done and MyBatis exceptions will be
   * thrown
   *
   * @param sqlSessionFactory
   * @param executorType
   * @param exceptionTranslator
   */
  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

  public SqlSessionFactory getSqlSessionFactory() {
    return this.sqlSessionFactory;
  }

  public ExecutorType getExecutorType() {
    return this.executorType;
  }

  public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
    return this.exceptionTranslator;
  }

  /**
   * {@inheritDoc}
   */
  public <T> T selectOne(String statement) {
    return this.sqlSessionProxy.<T> selectOne(statement);
  }

  /**
   * {@inheritDoc}
   */
  public <T> T selectOne(String statement, Object parameter) {
    return this.sqlSessionProxy.<T> selectOne(statement, parameter);
  }

  /**
   * {@inheritDoc}
   */
  public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
    return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey);
  }

  /**
   * {@inheritDoc}
   */
  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
    return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
  }

  /**
   * {@inheritDoc}
   */
  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
    return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
  }

  /**
   * {@inheritDoc}
   */
  public <E> List<E> selectList(String statement) {
    return this.sqlSessionProxy.<E> selectList(statement);
  }

  /**
   * {@inheritDoc}
   */
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.sqlSessionProxy.<E> selectList(statement, parameter);
  }

  /**
   * {@inheritDoc}
   */
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
  }

  /**
   * {@inheritDoc}
   */
  public void select(String statement, ResultHandler handler) {
    this.sqlSessionProxy.select(statement, handler);
  }

  /**
   * {@inheritDoc}
   */
  public void select(String statement, Object parameter, ResultHandler handler) {
    this.sqlSessionProxy.select(statement, parameter, handler);
  }

  /**
   * {@inheritDoc}
   */
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
  }

  /**
   * {@inheritDoc}
   */
  public int insert(String statement) {
    return this.sqlSessionProxy.insert(statement);
  }

  /**
   * {@inheritDoc}
   */
  public int insert(String statement, Object parameter) {
    return this.sqlSessionProxy.insert(statement, parameter);
  }

  /**
   * {@inheritDoc}
   */
  public int update(String statement) {
    return this.sqlSessionProxy.update(statement);
  }

  /**
   * {@inheritDoc}
   */
  public int update(String statement, Object parameter) {
    return this.sqlSessionProxy.update(statement, parameter);
  }

  /**
   * {@inheritDoc}
   */
  public int delete(String statement) {
    return this.sqlSessionProxy.delete(statement);
  }

  /**
   * {@inheritDoc}
   */
  public int delete(String statement, Object parameter) {
    return this.sqlSessionProxy.delete(statement, parameter);
  }

  /**
   * {@inheritDoc}
   */
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }

  /**
   * {@inheritDoc}
   */
  public void commit() {
    throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
  }

  /**
   * {@inheritDoc}
   */
  public void commit(boolean force) {
    throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
  }

  /**
   * {@inheritDoc}
   */
  public void rollback() {
    throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
  }

  /**
   * {@inheritDoc}
   */
  public void rollback(boolean force) {
    throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
  }

  /**
   * {@inheritDoc}
   */
  public void close() {
    throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
  }

  /**
   * {@inheritDoc}
   */
  public void clearCache() {
    this.sqlSessionProxy.clearCache();
  }

  /**
   * {@inheritDoc}
   *
   */
  public Configuration getConfiguration() {
    return this.sqlSessionFactory.getConfiguration();
  }

  /**
   * {@inheritDoc}
   */
  public Connection getConnection() {
    return this.sqlSessionProxy.getConnection();
  }

  /**
   * {@inheritDoc}
   *
   * @since 1.0.2
   *
   */
  public List<BatchResult> flushStatements() {
    return this.sqlSessionProxy.flushStatements();
  }

  /**
   * Proxy needed to route MyBatis method calls to the proper SqlSession got
   * from Spring's Transaction Manager
   * It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to
   * pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}.
   */
  private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      final SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }

}



这是sqlsessiontemplate的源码及注释
2
2
分享到:
评论
2 楼 windshome 2013-08-06  

因为每次请求相当启动一个数据库事务,本次请求可能连接N(N>=0)次数据库,如果我们每次数据库连接都要从重新获取(或者都要从池中获取)这是一种资源浪费,而且从事务角度来考虑也是不对的,这里是threadlocal典型的应用场景。

这种说法是不对的,事务是数据存取层的概念,而请求/响应是web交互层上的概念,分离开来很好,掺和在一起太乱了。

效率当然是考虑问题的一个重要方面,但是清晰是对架构的本质性要求。
1 楼 zh_harry 2013-08-02  
发帖之前去吃饭了,补充一句,因为sessiontemplate是线程安全的,而mybatis官方文档的说明讲的都是mybatis自己的实现,不要被官方说明所误导,我们用mybatis-spring的SqlSessionDaoSupport不会有线程问题。

相关推荐

    SpringBoot+MyBatis+SqlSession

    然后,配置mybatis-config.xml文件,定义数据源、事务管理器等信息。接着,创建Mapper接口和对应的XML映射文件,定义SQL语句。在SpringBoot启动类上使用`@MapperScan`注解扫描Mapper接口,让SpringBoot自动装配。 ...

    mybatis-3-mybatis-3.2.6

    1. **配置解析**:MyBatis的配置文件(mybatis-config.xml)被解析成Configration对象,这个对象包含了所有的全局配置信息,如数据源、事务管理器、Mappers等。解析过程主要由XMLConfigBuilder类完成。 2. **...

    mybatis-3-mybatis-3.3.0.zip

    在"Mybatis-3-mybatis-3.3.0.zip"这个压缩包中,我们可以找到MyBatis框架的3.3.0版本。这个版本是MyBatis发展历史上的一个重要里程碑,它包含了许多增强特性和改进,旨在提高开发效率和性能。 1. **核心组件**: -...

    mybatis获取SqlSession源代码流程

    mybatis获取SqlSession源代码流程

    springboot-mybatis-demo

    【标题】"springboot-mybatis-demo"是一个演示项目,展示了如何在Spring Boot框架下整合MyBatis和MySQL数据库。这个项目是基于现有的示例代码进行调整和改造的,旨在为学习者提供一个基础的实践环境。 【描述】该...

    mybatis-3-mybatis-3.1.0

    在"mybatis-3-mybatis-3.1.0"这个版本中,我们看到的是 MyBatis 框架的第3大版本的一个早期迭代。3.1.0 版本可能包含了自3.0版以来的一些增强和修复的bug,但具体改动需要查看官方发布日志来详细了解。 1. **配置...

    mybatis-3.5.9 源码(mybatis-3-mybatis-3.5.9.zip)

    在 MyBatis-3.5.9 版本中,我们可以深入源码,探究其内部机制和优化点。 1. **SqlSessionFactory 和 SqlSession** - SqlSessionFactory 是 MyBatis 的核心组件,它用于创建 SqlSession 对象,每次数据库操作都需要...

    Java-Persistence-with-MyBatis3

    这包括创建mybatis-config.xml配置文件,定义数据源、事务管理器以及Mappers。同时,还会讲解如何使用Spring框架集成MyBatis,实现依赖注入和事务管理。 在映射文件(Mapper XML文件)中,你可以定义SQL查询、插入...

    spring-mybatis-1.2.5-src.zip

    - **MyBatis-Spring**:这是一个连接Spring和MyBatis的桥梁,提供了一些关键的组件,如SqlSessionFactoryBean和SqlSessionTemplate,它们帮助Spring管理SqlSessionFactory和SqlSession。 - **...

    02-mybatis.zip

    在`mybatis-config.xml`中配置事务管理器,可以通过SqlSession的beginTransaction、commit和rollback方法控制事务的开始、提交和回滚。 9. **插件扩展** MyBatis允许自定义拦截器插件,可以对SQL执行过程进行拦截...

    spring-mybatis整合jar包,spring-mybatis整合jar包

    3. **mybatis-3.2.7.jar**:Mybatis是轻量级的ORM框架,它简化了SQL操作,允许开发者直接编写SQL语句并将其映射到Java对象上。在这个整合包中,Mybatis作为数据访问层的工具,与Spring的IoC容器配合,实现了DAO...

    mybatis-3-mybatis-3.4.6.tar

    在"压缩包子文件的文件名称列表"中,只提到了"mybatis-3-mybatis-3.4.6",这通常意味着解压后会得到一个包含MyBatis框架核心组件的目录结构,包括以下几个主要部分: 1. **lib** 目录:存放MyBatis的核心库和其他...

    spring-boot-starter-mybatis-spring-boot-3.0.0.tar.gz

    本文将围绕"spring-boot-starter-mybatis-spring-boot-3.0.0"这一主题,深入探讨Spring Boot 3.0.0版本中如何优雅地集成MyBatis,并分享如何在Linux环境下进行免费下载与部署。 首先,`spring-boot-starter-mybatis...

    项目配置文件( spring-mvc.xml spring-mybatis.xml web.xml log4j.properties)

    7. **mybatis-config.xml**: MyBatis的全局配置文件,定义了MyBatis的配置,包括类型别名、映射器文件的位置、数据库环境等。通过这个文件,MyBatis知道如何查找和处理SQL语句。 这些配置文件的组合使用,使得开发...

    mybatis-3-mybatis-3.5.8.zip源码

    在mybatis-3-mybatis-3.5.8.zip源码中,我们可以深入理解MyBatis的工作原理和设计模式。这个版本的MyBatis包含以下几个主要模块: 1. **SqlSessionFactoryBuilder**: 用于构建SqlSessionFactory,它是MyBatis的核心...

    mybatis-3-mybatis-3.5.7.tar.gz

    在压缩包文件名称列表中,我们看到的是"mybatis-3-mybatis-3.5.7.tar",这应该是未经过gzip压缩前的原始tar文件,包含了MyBatis 3.5.7的所有源码、文档、示例以及其他相关资源。 MyBatis 3.5.7版本可能包含以下关键...

    spring-mybatis整合jar包

    - 配置MyBatis的SqlSessionFactory:Spring会根据DataSource创建SqlSessionFactoryBean,这是MyBatis的核心对象,用于创建SqlSession。 - 配置MapperScannerConfigurer:扫描指定包下的Mapper接口,使得Spring能...

    spring-mybatis-spring-1.0.0-RC3.zip

    本文将围绕“spring-mybatis-spring-1.0.0-RC3.zip”这个压缩包,详细介绍Spring与MyBatis的整合过程,并提供相关资源的免费下载信息。 一、Spring与MyBatis的整合背景 在传统的Java开发中,数据访问层通常需要手动...

    spring-mybatis-2.0.0源码

    2. 配置MyBatis:创建MyBatis的配置文件(mybatis-config.xml),定义数据源、事务管理器、Mappers等信息。 3. 创建SqlSessionFactoryBean:在Spring的配置文件中,创建一个`SqlSessionFactoryBean`,通过`...

    lijie_study-mybatis-3-master.zip

    在"lijie_study-mybatis-3-master.zip"这个压缩包中,包含了MyBatis 3项目的源码,这为我们深入理解MyBatis的工作原理提供了宝贵的资料。以下是关于MyBatis核心知识点的详细解析: 1. **配置文件**: - `mybatis-...

Global site tag (gtag.js) - Google Analytics