MyBatis原理--插入操作

mybatis整合shardingjdbc的实现原理(接入点) 一只在纠结mybatis和sdjdbc的接入点在哪,往回看的时候发现自己对spring怎么结合mybaits的原理也不熟悉,路漫漫啊。。。。 spring+mybatis原理:https://blog.youkuaiyun.com/lihuayong/article/details/83934744 这个里面介绍里Configuration,environment对象怎么被创建的,里面传入的datasourc... 阅读详情

上一篇中已经解析了配置文件,并创建了DefaultSqlSessionFactory对象,现在来看一下从DefaultSqlSessionFactory对象中创建一个sqlSession并执行一次insert操作的过程。(MyBatis版本:3.2)

首先是获取SqlSession的过程:

public class DefaultSqlSessionFactory implements SqlSessionFactory {
  private final Configuration configuration;
  private final TransactionFactory managedTransactionFactory;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
    this.managedTransactionFactory = new ManagedTransactionFactory();
  }

  public SqlSession openSession() {
    //defaultExecutorType = ExecutorType.SIMPLE
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    try {
      //从environment中获取dataSource和transactionFactory
      final Environment environment = configuration.getEnvironment();
      final DataSource dataSource = getDataSourceFromEnvironment(environment);
      TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      Connection connection = dataSource.getConnection();
      if (level != null) {
        connection.setTransactionIsolation(level.getLevel());
      }

      //如果日志设置到debug级别,则为connection设置代理,并对Statement,PrepareStatement,ResultSet对象设置代理,主要作用也就是获取更多的调试信息,打印更详细的log。
      connection = wrapConnection(connection);
      Transaction tx = transactionFactory.newTransaction(connection, autoCommit);
      Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (SQLException e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //从environment中获取dataSource和transactionFactory
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

      //此处返回SimpleExecutor, executor是sql语句执行的关键。
      final Executor executor = configuration.newExecutor(tx, execType, autoCommit);

      //将生成的executor赋值给sqlSession
      return new DefaultSqlSession(configuration, executor);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

看一下executor的生成过程:

public class Configuration {
    //ExecutorType用的是默认的,ExecutorType.SIMPLE
  public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }

    //如果使用cache,CachingExeutor对真正的executor做了简单的封装。
    if (cacheEnabled) {
      executor = new CachingExecutor(executor, autoCommit);
    }

    //这里将executor添加到拦截器链中
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  //该方法中拦截器对executor做了代理,然后后续的拦截器对面前的拦截器做代理。
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

}

//MyBatis并没有官方的拦截器,所以无法看到在plugin方法中官方是怎么做的,通常在此方法中会调用Plugin.wrap()方法。
public class Plugin implements InvocationHandler {

  private Object target;
  private Interceptor interceptor;
  private Map<Class<?>, Set<Method>> signatureMap;

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  //这里第一个参数是被代理的对象,第二个参数是代理对象。
  //最开始的被代理对象是executor,被拦截器代理,然后拦截器本身也会变成被代理的对象,被后面的拦截器代理
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
        //这里是真正设置代理的地方
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        //intercept()方法是代理逻辑具体执行的地方,在被代理对象的method执行前后,或者围绕着被代理对象的method的执行过程,做一些事情。
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
}

//至此,SqlSession创建过程结束。

下面看一下SqlSesssion执行insert操作的过程:

public class DefaultSqlSession implements SqlSession {

  private Configuration configuration;
  private Executor executor;

  private boolean dirty;


  public DefaultSqlSession(Configuration configuration, Executor executor) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
  }

  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }

  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      //此时获取MappedStatement的时候,如果还有未解析的resultMap,parameterMap或者cache,则继续解析。
      MappedStatement ms = configuration.getMappedStatement(statement);

      //如果调用的statement使用了cache,那么会先清除所有cache,然后再交给SimpleExecutor去处理(Executor默认类型是SimpleExecutor)。
      //一个mapper文件中如果声明了cache,那么select操作默认会使用这个cache,insert、update和delete操作默认会刷新这个cache。
      //此处就不再看CacheExecutor的代码了,直接进入SimpleExecutor。SimpleExecutor的update方法继承的是父类BaseExecutor的。
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

SimpleExecutor中逻辑:

//先看一眼BaseExecutor中的update方法
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    //清除一下本地缓存,主要逻辑在doUpdate方法中
    clearLocalCache();
    return doUpdate(ms, parameter);
  }

 //SimpleExecutor
public class SimpleExecutor extends BaseExecutor {
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //先获取statementHandler
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      //再获取statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      //然后执行
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }
}

//先来看一下Configuration.newStatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //可以看到这边new的是一个RoutingStatementHandler,从名字上就可以看出这家伙不是真正做事情的,是做分发的。
    //所以在RoutingStatementHandler的构造方法中,根据MappedStatement的类型(默认为Prepared类型),构造真正做事情的StatementHandler。
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);

    //statementHandler在此处的待遇和之前的executor一样,都是被interceptor代理。
    //PS:所以如果想用拦截器做什么事情,可以拦截executor或者statementHandler的方法。
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}

//PreparedStatementHandler的构造方法直接调用了父类BaseStatementHandler的构造方法:
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      //insert操作传过来的boundSql为null,肯定会走到这里。
      //先生成key
      generateKeys(parameterObject);
      //获取boundSql
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

    //简单的封装,赋值
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

//生成key的逻辑
protected void generateKeys(Object parameter) {
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    ErrorContext.instance().store();
    keyGenerator.processBefore(executor, mappedStatement, null, parameter);
    ErrorContext.instance().recall();
}

//看一下SelectKeyGenerator是怎么做的
public class SelectKeyGenerator implements KeyGenerator {
  public static final String SELECT_KEY_SUFFIX = "!selectKey";
  private boolean executeBefore;
  private MappedStatement keyStatement;

  public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
    this.executeBefore = executeBefore;
    this.keyStatement = keyStatement;
  }

  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    //如果是在执行前生成key
    if (executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (!executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
      final Configuration configuration = ms.getConfiguration();
      if (parameter != null) {
        String keyStatementName = ms.getId() + SELECT_KEY_SUFFIX;
        if (configuration.hasStatement(keyStatementName)) {

          if (keyStatement != null && keyStatement.getKeyProperties() != null) {
            String keyProperty = keyStatement.getKeyProperties()[0]; //just one key property is supported
            //MetaObject是一个工具类,可以获取封装的对象的属性的值或者设置属性的值
            //获取设置的key对应的属性,以及查看parameter是否能够为该属性设置值
            final MetaObject metaParam = configuration.newMetaObject(parameter);
            if (keyProperty != null && metaParam.hasSetter(keyProperty)) {
              // Do not close keyExecutor.
              // The transaction will be closed by parent executor.
              //此处执行一次查询操作,查询操作执行过程在其他文章中再说,这里先简单理解为执行一次查询操作获取到key就好了。
              Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
              List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
              if (values.size() > 1) {
                throw new ExecutorException("Select statement for SelectKeyGenerator returned more than one value.");
              }
              metaParam.setValue(keyProperty, values.get(0));
            }
          }
        }
      }
    } catch (Exception e) {
      throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
    }
  }

}

//再看是如何获取BoundSql的
public final class MappedStatement {
  public BoundSql getBoundSql(Object parameterObject) {
    //此处的sqlSource是DynamicSqlSource
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

    //如果parameterMappings为空或者size <= 0,发生这种状况的话,个人理解是,sql语句中没有变量或者所有的变量都是${}这样表示的
    if (parameterMappings == null || parameterMappings.size() <= 0) {
        //此时boundSql的parameterMappings取值为parameterMap.getParameterMappings。
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }
}

//SqlSource获取BoundSql的逻辑
public class DynamicSqlSource implements SqlSource {
  public BoundSql getBoundSql(Object parameterObject) {
    //此处相当于对parameterObject做了一层封装。
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    //sqlNode应用context,这里也处理了动态sql语句中<if>, <foreach>这些情况。
    //处理一些表达式的时候用到了OGNL
    //另外,${}这样表示的变量,在这里被变量的具体value替换了。
    rootSqlNode.apply(context);

    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    //根据sql语句生成SqlSource对象
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    //获取boundSql,sqlSource.getBoundSql方法中new了一个BoundSql,其构造方法也只是做了简单的赋值。
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);

    //这个地方相当于是说把parameterObject对象里,参数和值的对应关系保存在additionalParameter中。
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }
}

public class SqlSourceBuilder extends BaseBuilder {
  public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    //此处得到完成的sql语句,#{}这样表示的变量,会被"?"替换,具体对应的参数名字被放在handler.parameterMappings中
    String sql = parser.parse(originalSql);
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }
}

至此,获取statementHandler的逻辑结束。

再看一眼SimpleExecutor.doUpdate做的事情

public class SimpleExecutor extends BaseExecutor {
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);

      //接下来看看这一部分逻辑,获取Statement。
      stmt = prepareStatement(handler, ms.getStatementLog());

      //执行具体的update操作。
      return handler.update(stmt);
    } finally {
        //关闭Statement
      closeStatement(stmt);
    }
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //这部分代码是不是看起来就有点眼熟的赶脚!
    //从transaction中获取connection,如果log是debug级别,会为connection生成一个能够打印比较多log的代理。
    Connection connection = getConnection(statementLog);
    //handler为RoutingStatementHandler,它作为PreparedStatementHandler的代理。
    //初始化statement
    stmt = handler.prepare(connection);
    //设置参数
    handler.parameterize(stmt);
    return stmt;
  }
}

//初始化statement的过程
public class PreparedStatementHandler extends BaseStatementHandler {

    //prepare方法继承的是父类BaseStatementHandler的
  public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //初始化prepareStatement
      statement = instantiateStatement(connection);
      //设置语句超时时间,如果该条语句设置的超时时间为空,则从configuration获取。
      setStatementTimeout(statement);
      //设置语句每次返回结果条数,如果为空则忽略。
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
}

//DefaultParameterHandler.setParameters
public void setParameters(PreparedStatement ps) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    //parameterMappings中保存着动态sql中每个参数的名字,additionalParameter中保存着参数对应的值。
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            value = metaObject == null ? null : metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          if (typeHandler == null) {
            throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId());
          }
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
          //真正为PrepareStatement设置参数的地方。
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        }
      }
    }
  }

上面是获取statement的过程,下面就要具体执行update操作了,在PrepareStatementHandler中:

public class PreparedStatementHandler extends BaseStatementHandler {

  public int update(Statement statement) throws SQLException {
    //这几句都很熟
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    int rows = ps.getUpdateCount();

    //这里同样是获取key的逻辑
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    //如果keySelect语句设置的是在sql执行之后在执行,那么此处去或得下一个key。(具体逻辑和之前的一样)
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    //返回影响的条数。
    return rows;
  }
}

至此,插入操作执行结束。

图片边界矢量化-地理配准 无地理和投影信息的JPG图片,通过ArcGIS进行地理配准(Georeferencing)完成数字化边界。 阅读详情

相关推荐

Linux-ftrace-双nop机制的实现

linux-ftrace内核追踪-双nop机制的实现原理

qq_43707773的博客 1071

面试官:你分析过mybatis工作原理吗?

Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去。本文建立在Spring+SpringMVC+Mybatis整合的项目之上。我将其工作原理分为六个部分:读取核心配置文件并返回InputStream流对象。根据InputStream流对象解析出Configuration对象,然后创建SqlSessionFactory工厂对象根据一系列属性从SqlSessionFactor...

Java笔记虾 508

从TI官方手册里挖细节:TPS5430负压电路那个不起眼却救命的Cd电容

本文深入解析TPS5430负压电路中关键Cd电容的作用与设计要点,揭示其在DC-DC电源设计中的重要性。通过对比TI手册和实战案例,详细阐述Cd电容在高频电流回路、能量缓冲和噪声抑制中的核心功能,并提供选型与布局指南,帮助工程师避免芯片烧毁风险,提升电路稳定性。

weixin_27102543的博客 357

mybatis

16.springboot自定义starter。spring的IOC在具体业务实现。springboot的自动装配原理。2.mybatis注解实现增删改查。17.springboot的注解。3.mybatis的动态sql。13.spring的bean。1.xml方式实现增删改查。spring的AOP实现。14.spring的IOC。11.spring的事务。spring传播行为。

B__B_B的博客 2049

FreeRTOS-Plus-CLI使用笔记

FreeRTOS-Plus-CLI使用笔记 一、简介 详细介绍可参考FreeRTOS-Plus-CLI官网地址入口 FreeRTOS+CLI(Command Line Interface, 命令行接口)提供了一种简单、小型、可扩展且RAM高效的方法,使FreeRTOS应用程序能够处理命令行输入。添加一条指令的步骤如下: 提供一个实现命令功能的函数 将命令映射到该功能函数 注册命令 运行命令解释器 命令行调试接口对于排查分析出现的问题提供了很好的接口,可以通过查看传感器信息判断传感器是否正常,可以查看内置

ApexYuan 6963

从MFCC到GMM:手把手教你用Python实现说话人识别(附完整代码)

本文详细介绍了如何使用Python实现说话人识别系统,从MFCC特征提取到GMM模型训练,涵盖完整的技术流程和代码示例。通过梅尔频率倒谱系数(MFCC)和高斯混合模型(GMM)的结合,构建高效的说话人识别系统,适用于身份认证和智能交互场景。文章还探讨了从传统方法到深度学习的进阶话题,如i-vector和x-vector技术。

o1p2q3r的博客 200

mybatis的增删改操作

openSession(): 默认开启事务,进行增删改操作后需要用sqlSession.commit();openSession(true): 可以设置为自动提交事务。1. 编写接口方法:Mapper接口。参数:除了id之外的所有数据。注意:Mybatis事务。返回结果使用void。2. 编写sql语句。

2301_77007146的博客 713

ios生成海报使用dom-to-image中toPng报错isTrusted:true

主要是ios中safari浏览器解析错误,引起的报错,主要引起原因toPng会调用toSvg,toSvg直接将html标签内容通过svg中foreignObject标签直接包裹,然后转base64, 通过data:image/svg+xml;解决办法:二次生成(调用两次),判断ios or 安卓,生成第一次生成,不用参数,例如:scale: 3,在第二次生成再进行清晰度操作,避免多次生成,引起性能消耗。以上种种很多案例,使用canvase进行转换的,标签丢失,直接toPng版本的,兼容性问题。

weixin_44601570的博客 2105

mybatis-plus批量insert原理探究

概述 在做某些业务的时候,涉及到批量增删改,可以直接调用mybatis-plus的增删改方法,不用自己去写了,能够节省很多的时间,下面我们对增删改的原理做一个简单的探究,并对比总结 批量增 我们看一下mybatis-plus中批量写入的源代码, 其中有这样一句话: String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE); 从命名可以看出这是从SqlMethod类中获取到写入一条的sqlStatement,我们点进去看,果然,INSERT_ONE是一

weixin_40598838的博客 5401

基于Python的房地产评估数据抓取与分析 —— 房价走势与市场报告

在房地产行业中,了解和分析房产价格的变化趋势、各个地区的房价差异以及市场评价报告,可以帮助投资者、购房者以及政策制定者做出更加明智的决策。本文介绍了如何使用Python爬虫技术抓取房地产平台上的房产评估报告、价格走势等数据,并对抓取的数据进行分析。通过这些技术,您可以自动化抓取房地产平台的数据,分析房价走势,获取市场报告,为房地产投资决策提供数据支持。假设我们要从链家网抓取某个房产的价格、房产描述以及历史成交价格,首先需要获取目标房产页面的URL,并发送HTTP请求。伪装为常见浏览器,避免被识别为爬虫。

2201_76125261的博客 846

《计算机视觉:从入门到精通》技术手册 第1章 计算机视觉概述与历史演进

视觉感知与机器视觉在工程实践中常被混用,但二者存在本质差异。视觉感知强调对光学信号的语义理解与场景解释,核心在于从二维投影重建三维世界的物理与语义属性。该领域关注遮挡处理、光照变化、材质反射等复杂因素的建模,其目标是实现类似人类视觉系统的场景理解能力。机器视觉则侧重于工业场景中的精密测量与控制,通常基于结构化光源、已知几何约束与标定参数,执行边缘检测、尺寸测量、位置定位等任务。前者面向开放环境的通用理解,后者面向受控环境的精确执行。

u012133341的博客 269

SelectKey returned more than one value

问题:使用mybatis进行插入操作时一直报错,报错具体信息如下图,但是数据插入成功了,其他查询更新操作不会有问题。 解决方案: 网上百度好多方法是说查询操作时返回结果多于期望的一条,但姐姐是插入操作呢,蒙圈。 猜测应该是和主键自动生成有关系了,查看数据表对应的实体类,发现主键字段使用了注解 @GeneratedValue(strategy = GenerationType.IDENT...

sinat_24296421的博客 3544

【CSP-S 提高组】考试大纲集合

【CSP-S提高组】考试大纲集合

weixin_42596089的博客 7254

MySQL一次到底插入多少条数据合适啊?

最后,我们来总结下扒。在本文中,我为你提供一个参与面试的视角,帮助你理解和优化数据库的插入操作。不管你是数据库新手还是经验丰富的开发者,我希望这些建议能够为你在实际应用中带来价值。如有任何疑问或建议,请随时与我分享!

weixin_64713614的博客 1865

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

直接问懵圈,MySQL一次批量插入多少条数据性能最佳?

????这是一个或许对你有用的社群????一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:《项目实战(视频)》:从书中学,往事中“练”《互联网高频面试题》:面朝简历学习,春暖花开《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题《精进 Java 学习指南》:系统学习,互联网主流技术栈《必读 Java 源码专栏》:知其然,知其所以然????这是一个或许...

weixin_44421461的博客 201

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Mybatis 实现原理

目录 一、原理流程图 二、 其余补充 Mybatis三种的执行器 SIMPLE方式 REUSE方式 BATCH 三、常见问题 一、原理流程图 1、程序启动 读取配置文件mybatis-config.xml的内容 2、sqlSessionFactoryBulider().bulid() 生成一个Configation 对象(里面带有配置文件的属性) 3、通过configuration对象打开一个SqlSession对象 4、sqlSession通过getMapper去读取mapp.

qq_37203082的博客 648

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Mybatis优化:使用batch模式实现批量插入/更新

Mybatis批量插入优化

manba_out的博客 1万+

【MyBatis批量插入性能飞跃】:掌握ON DUPLICATE KEY UPDATE的5大核心技巧

提升MyBatis批量插入效率?掌握ON DUPLICATE KEY UPDATE的5大技巧,解决唯一键冲突场景下的数据 Upsert 需求。涵盖SQL优化、参数映射、性能调优等核心方法,显著提升数据库写入速度与稳定性,值得收藏。

VarChat的博客 926

MyBatis-Plus#逻辑删除(@TableLogic)

1. MyBatis-Plus逻辑删除使用 2. MyBatis-Plus逻辑删除原理 3. 多次逻辑删除 4. 逻辑删除和唯一索引

每天进步一小步 810

【Java开发高手必修课】:MyBatis批量插入VALUES多个值的底层原理与实战方案

掌握MyBatis批量插入values多个值的高效写法,解决大批量数据入库性能瓶颈。适用于日志处理、报表生成等场景,通过foreach标签拼接SQL,结合ExecutorType.BATCH提升插入效率3-5倍。深入解析底层原理与避坑指南,值得收藏。

ProcePerch的博客 441

Mybatis 是如何进行分页的?分页插件的原理是什么?

Mybatis 是如何进行分页的?分页插件的原理是什么? Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页。可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数 Mybatis是如何将sql执行结果

G_whang的博客 5057

mybatis-plus逻辑删除

逻辑删除是一种优雅的数据管理策略,它通过在数据库中标记记录为“已删除”而非物理删除,来保留数据的历史痕迹,同时确保查询结果的整洁性。通过以上步骤,你可以轻松地在 MyBatis-Plus 中实现逻辑删除功能,提高数据管理的灵活性和安全性。MyBatis-Plus 的逻辑删除功能会在执行数据库操作时自动处理逻辑删除字段。同样,逻辑未删除值默认为 0,逻辑已删除值默认为 1。类型,可以配置逻辑未删除值为 0,已删除值可以使用函数如。在实体类中,对应数据库表的逻辑删除字段上添加。类型,可以配置逻辑未删除值为。

ruan114514的博客 908

MyBatis-Plus的CRUD操作

通用CRUD 通过Mybatis-Plus与MyBatis、Spring、SpringBoot框架的简单整合的学习,我们了解到通过继承BaseMapper就可以获取到各种各样的单表操作,接下来我们继续深入学习MyBatis-Plus的通用CRUD操作 数据插入 测试用例 这里使用的环境是SpringBoot+MyBatis-Plus中的代码环境 插入一条数据 package com.zg.mp; import com.zg.mp.mapper.UserMapper; import com.zg.mp.po

好好学习 584

通过源码分析Mybatis是如何返回数据库生成的自增主键值?

在Mybatis中,执行insert操作时,如果我们希望返回数据库生成的自增主键值,那么就需要使用到KeyGenerator对象。需要注意的是,KeyGenerator的作用,是返回数据库生成的自增主键值,而不是生成数据库的自增主键值。返回的主键值放到哪儿呢?放到parameter object的主键属性上。下面看看其接口定义。接口定义还是比较简单的,就是在insert前、insert后,策略处理主键值。

m0_73603837的博客 742

activiti学习(十四)——activiti数据库存储(三)——实体管理类封装mybatis调用原理

上两篇文章主要分析activiti数据库相关初始化、sessionFactory初始化及创建实体管理类的原理。实体管理类封装了activiti底层增删查改操作。上层命令类程序不需要直接去调用mybatis的sqlSession,通过实体管理类间接调用。本文通过例子分析实体管理类是如何调用mybaits的。 我们以DeploymentEntityManager为例: public void i...

sadoshi的专栏 1531
上一篇: MyBatis原理--配置解析
下一篇: MyBatis原理--查询操作
java_4_ever
博客等级 码龄16年 8粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值