mybatis里头给sqlSession指定执行哪条sql的时候,有两种方式,一种是写mapper的xml的namespace+statementId,如下:

  1. public Student findStudentById(Integer studId) {
  2. logger.debug("Select Student By ID :{}", studId);
  3. SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession();
  4. try {
  5. return sqlSession.selectOne("com.mybatis3.StudentMapper.findStudentById", studId);
  6. } finally {
  7. sqlSession.close();
  8. }
  9. }

另外一种方法是指定mapper的接口:

  1. public Student findStudentById(Integer studId) {
  2. logger.debug("Select Student By ID :{}", studId);
  3. SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession();
  4. try {
  5. StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
  6. return studentMapper.findStudentById(studId);
  7. } finally {
  8. sqlSession.close();
  9. }
  10. }

一般的话,比较推荐第二种方法,因为手工写namespace和statementId极大增加了犯错误的概率,而且也降低了开发的效率。

问题

mapper的实现类如何生成

如果使用mapper接口的方式,问题来了,这个是个接口,通过sqlSession对象get出来的一定是个实现类,问题是,我们并没有手工去写实现类,那么谁去干了这件事情呢?答案是mybatis通过JDK的动态代理方式,在启动加载配置文件时,根据配置mapper的xml去生成。

mybatis-spring帮忙做了什么

自动open和close session

一、mapper代理类是如何生成的

启动时加载解析mapper的xml

如果不是集成spring的,会去读取<mappers>节点,去加载mapper的xml配置

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
  3. <configuration>
  4. <settings>
  5. <setting name="cacheEnabled" value="true"/>
  6. <setting name="lazyLoadingEnabled" value="true"/>
  7. <setting name="multipleResultSetsEnabled" value="true"/>
  8. <setting name="useColumnLabel" value="true"/>
  9. <setting name="useGeneratedKeys" value="false"/>
  10. <setting name="defaultExecutorType" value="SIMPLE"/>
  11. <setting name="defaultStatementTimeout" value="2"/>
  12. </settings>
  13. <typeAliases>
  14. <typeAlias alias="CommentInfo" type="com.xixicat.domain.CommentInfo"/>
  15. </typeAliases>
  16. <environments default="development">
  17. <environment id="development">
  18. <transactionManager type="JDBC"/>
  19. <dataSource type="POOLED">
  20. <property name="driver" value="com.mysql.jdbc.Driver"/>
  21. <property name="url" value="jdbc:mysql://localhost:3306/demo"/>
  22. <property name="username" value="root"/>
  23. <property name="password" value=""/>
  24. </dataSource>
  25. </environment>
  26. </environments>
  27. <mappers>
  28. <mapper resource="com/xixicat/dao/CommentMapper.xml"/>
  29. </mappers>
  30. </configuration>

如果是集成spring的,会去读spring的sqlSessionFactory的xml配置中的mapperLocations,然后去解析mapper的xml

  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  2. <property name="dataSource" ref="dataSource"/>
  3. <!-- 配置mybatis配置文件的位置 -->
  4. <property name="configLocation" value="classpath:mybatis-config.xml"/>
  5. <property name="typeAliasesPackage" value="com.xixicat.domain"/>
  6. <!-- 配置扫描Mapper XML的位置 -->
  7. <property name="mapperLocations" value="classpath:com/xixicat/dao/*.xml"/>
  8. </bean>

然后绑定namespace(XMLMapperBuilder)

  1. private void bindMapperForNamespace() {
  2. String namespace = builderAssistant.getCurrentNamespace();
  3. if (namespace != null) {
  4. Class<?> boundType = null;
  5. try {
  6. boundType = Resources.classForName(namespace);
  7. } catch (ClassNotFoundException e) {
  8. //ignore, bound type is not required
  9. }
  10. if (boundType != null) {
  11. if (!configuration.hasMapper(boundType)) {
  12. // Spring may not know the real resource name so we set a flag
  13. // to prevent loading again this resource from the mapper interface
  14. // look at MapperAnnotationBuilder#loadXmlResource
  15. configuration.addLoadedResource("namespace:" + namespace);
  16. configuration.addMapper(boundType);
  17. }
  18. }
  19. }
  20. }

这里先去判断该namespace能不能找到对应的class,若可以则调用

  1. configuration.addMapper(boundType);

configuration委托给MapperRegistry:

  1. public <T> void addMapper(Class<T> type) {
  2. mapperRegistry.addMapper(type);
  3. }

生成该mapper的代理工厂(MapperRegistry)

  1. public <T> void addMapper(Class<T> type) {
  2. if (type.isInterface()) {
  3. if (hasMapper(type)) {
  4. throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
  5. }
  6. boolean loadCompleted = false;
  7. try {
  8. knownMappers.put(type, new MapperProxyFactory<T>(type));
  9. // It's important that the type is added before the parser is run
  10. // otherwise the binding may automatically be attempted by the
  11. // mapper parser. If the type is already known, it won't try.
  12. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  13. parser.parse();
  14. loadCompleted = true;
  15. } finally {
  16. if (!loadCompleted) {
  17. knownMappers.remove(type);
  18. }
  19. }
  20. }
  21. }

这里的重点就是MapperProxyFactory类:

  1. public class MapperProxyFactory<T> {
  2. private final Class<T> mapperInterface;
  3. private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
  4. public MapperProxyFactory(Class<T> mapperInterface) {
  5. this.mapperInterface = mapperInterface;
  6. }
  7. public Class<T> getMapperInterface() {
  8. return mapperInterface;
  9. }
  10. public Map<Method, MapperMethod> getMethodCache() {
  11. return methodCache;
  12. }
  13. @SuppressWarnings("unchecked")
  14. protected T newInstance(MapperProxy<T> mapperProxy) {
  15. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  16. }
  17. public T newInstance(SqlSession sqlSession) {
  18. final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  19. return newInstance(mapperProxy);
  20. }
  21. }

getMapper的时候生成mapper代理类

  1. @SuppressWarnings("unchecked")
  2. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  3. final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  4. if (mapperProxyFactory == null) {
  5. throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  6. }
  7. try {
  8. return mapperProxyFactory.newInstance(sqlSession);
  9. } catch (Exception e) {
  10. throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  11. }
  12. }

new出来MapperProxy

  1. public T newInstance(SqlSession sqlSession) {
  2. final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  3. return newInstance(mapperProxy);
  4. }
  5. @SuppressWarnings("unchecked")
  6. protected T newInstance(MapperProxy<T> mapperProxy) {
  7. return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  8. }

这里给代理类注入了sqlSession

MapperProxy实现InvocationHandler接口进行拦截代理

  1. public class MapperProxy<T> implements InvocationHandler, Serializable {
  2. private static final long serialVersionUID = -6424540398559729838L;
  3. private final SqlSession sqlSession;
  4. private final Class<T> mapperInterface;
  5. private final Map<Method, MapperMethod> methodCache;
  6. public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
  7. this.sqlSession = sqlSession;
  8. this.mapperInterface = mapperInterface;
  9. this.methodCache = methodCache;
  10. }
  11. @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. if (Object.class.equals(method.getDeclaringClass())) {
  14. try {
  15. return method.invoke(this, args);
  16. } catch (Throwable t) {
  17. throw ExceptionUtil.unwrapThrowable(t);
  18. }
  19. }
  20. final MapperMethod mapperMethod = cachedMapperMethod(method);
  21. return mapperMethod.execute(sqlSession, args);
  22. }
  23. private MapperMethod cachedMapperMethod(Method method) {
  24. MapperMethod mapperMethod = methodCache.get(method);
  25. if (mapperMethod == null) {
  26. mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
  27. methodCache.put(method, mapperMethod);
  28. }
  29. return mapperMethod;
  30. }
  31. }

这里的代理拦截,主要是寻找到MapperMethod,通过它去执行SQL。

MapperMethod委托给SqlSession去执行sql

  1. public Object execute(SqlSession sqlSession, Object[] args) {
  2. Object result;
  3. if (SqlCommandType.INSERT == command.getType()) {
  4. Object param = method.convertArgsToSqlCommandParam(args);
  5. result = rowCountResult(sqlSession.insert(command.getName(), param));
  6. } else if (SqlCommandType.UPDATE == command.getType()) {
  7. Object param = method.convertArgsToSqlCommandParam(args);
  8. result = rowCountResult(sqlSession.update(command.getName(), param));
  9. } else if (SqlCommandType.DELETE == command.getType()) {
  10. Object param = method.convertArgsToSqlCommandParam(args);
  11. result = rowCountResult(sqlSession.delete(command.getName(), param));
  12. } else if (SqlCommandType.SELECT == command.getType()) {
  13. if (method.returnsVoid() && method.hasResultHandler()) {
  14. executeWithResultHandler(sqlSession, args);
  15. result = null;
  16. } else if (method.returnsMany()) {
  17. result = executeForMany(sqlSession, args);
  18. } else if (method.returnsMap()) {
  19. result = executeForMap(sqlSession, args);
  20. } else {
  21. Object param = method.convertArgsToSqlCommandParam(args);
  22. result = sqlSession.selectOne(command.getName(), param);
  23. }
  24. } else if (SqlCommandType.FLUSH == command.getType()) {
  25. result = sqlSession.flushStatements();
  26. } else {
  27. throw new BindingException("Unknown execution method for: " + command.getName());
  28. }
  29. if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
  30. throw new BindingException("Mapper method '" + command.getName()
  31. + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  32. }
  33. return result;
  34. }

其实这里就回到了第一种模式,该模式是直接指定了statement的Id(这里是command.getName()),而通过mapper的接口方式,则多了这么步骤,最后通过MapperMethod,给sqlSession传入statement的id。

sqlSession其实自己也不执行sql,它只是mybatis对外公布的一个api入口,具体它委托给了executor去执行sql。

什么时候去getMapper

  • 手工get,比如

    1. public void createStudent(Student student) {
    2. SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession();
    3. try {
    4. StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    5. studentMapper.insertStudent(student);
    6. sqlSession.commit();
    7. } finally {
    8. sqlSession.close();
    9. }
    10. }
  • 集成spring的话
    在spring容器给指定的bean注入mapper的时候get出来(见MapperFactoryBean的getObject方法)

二、mybatis-spring帮忙做了什么

通过MapperScannerConfigurer将mapper适配成spring bean

  1. <!-- 配置扫描Mapper接口的包路径 -->
  2. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  3. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
  4. <property name="basePackage" value="com.xixicat.dao"/>
  5. </bean>

这里使用 MapperFactoryBean将Mapper接口配置成 Spring bean 实体同时注入sqlSessionFactory。

MapperScannerConfigurer给每个mapper生成对应的MapperFactoryBean

  1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
  2. if (this.processPropertyPlaceHolders) {
  3. processPropertyPlaceHolders();
  4. }
  5. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  6. scanner.setAddToConfig(this.addToConfig);
  7. scanner.setAnnotationClass(this.annotationClass);
  8. scanner.setMarkerInterface(this.markerInterface);
  9. scanner.setSqlSessionFactory(this.sqlSessionFactory);
  10. scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  11. scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  12. scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  13. scanner.setResourceLoader(this.applicationContext);
  14. scanner.setBeanNameGenerator(this.nameGenerator);
  15. scanner.registerFilters();
  16. scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  17. }

委托给ClassPathMapperScanner去scan

  1. public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  2. Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
  3. if (beanDefinitions.isEmpty()) {
  4. logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  5. } else {
  6. for (BeanDefinitionHolder holder : beanDefinitions) {
  7. GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
  8. if (logger.isDebugEnabled()) {
  9. logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
  10. + "' and '" + definition.getBeanClassName() + "' mapperInterface");
  11. }
  12. // the mapper interface is the original class of the bean
  13. // but, the actual class of the bean is MapperFactoryBean
  14. definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
  15. definition.setBeanClass(MapperFactoryBean.class);
  16. definition.getPropertyValues().add("addToConfig", this.addToConfig);
  17. boolean explicitFactoryUsed = false;
  18. if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
  19. definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
  20. explicitFactoryUsed = true;
  21. } else if (this.sqlSessionFactory != null) {
  22. definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
  23. explicitFactoryUsed = true;
  24. }
  25. if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
  26. if (explicitFactoryUsed) {
  27. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  28. }
  29. definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
  30. explicitFactoryUsed = true;
  31. } else if (this.sqlSessionTemplate != null) {
  32. if (explicitFactoryUsed) {
  33. logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
  34. }
  35. definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
  36. explicitFactoryUsed = true;
  37. }
  38. if (!explicitFactoryUsed) {
  39. if (logger.isDebugEnabled()) {
  40. logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
  41. }
  42. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  43. }
  44. }
  45. }
  46. return beanDefinitions;
  47. }

这里出现了MapperFactoryBean的身影,然后判断配置文件是指定注入sqlSessionFactory,还是sqlTemplate(二者不能同时指定,这里是指定了sqlSessionFactory)。这里通过sqlSessionFactoryBeanName暂时先注入引用,因为此时还在给spring托管的bean进行create,不确定sqlSessionFactory是否已经被创建。

关于MapperFactoryBean

  1. public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  2. private Class<T> mapperInterface;
  3. private boolean addToConfig = true;
  4. /**
  5. * Sets the mapper interface of the MyBatis mapper
  6. *
  7. * @param mapperInterface class of the interface
  8. */
  9. public void setMapperInterface(Class<T> mapperInterface) {
  10. this.mapperInterface = mapperInterface;
  11. }
  12. /**
  13. * If addToConfig is false the mapper will not be added to MyBatis. This means
  14. * it must have been included in mybatis-config.xml.
  15. * <p>
  16. * If it is true, the mapper will be added to MyBatis in the case it is not already
  17. * registered.
  18. * <p>
  19. * By default addToCofig is true.
  20. *
  21. * @param addToConfig
  22. */
  23. public void setAddToConfig(boolean addToConfig) {
  24. this.addToConfig = addToConfig;
  25. }
  26. /**
  27. * {@inheritDoc}
  28. */
  29. @Override
  30. protected void checkDaoConfig() {
  31. super.checkDaoConfig();
  32. notNull(this.mapperInterface, "Property 'mapperInterface' is required");
  33. Configuration configuration = getSqlSession().getConfiguration();
  34. if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
  35. try {
  36. configuration.addMapper(this.mapperInterface);
  37. } catch (Throwable t) {
  38. logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
  39. throw new IllegalArgumentException(t);
  40. } finally {
  41. ErrorContext.instance().reset();
  42. }
  43. }
  44. }
  45. /**
  46. * {@inheritDoc}
  47. */
  48. public T getObject() throws Exception {
  49. return getSqlSession().getMapper(this.mapperInterface);
  50. }
  51. /**
  52. * {@inheritDoc}
  53. */
  54. public Class<T> getObjectType() {
  55. return this.mapperInterface;
  56. }
  57. /**
  58. * {@inheritDoc}
  59. */
  60. public boolean isSingleton() {
  61. return true;
  62. }
  63. }

注意这里继承了SqlSessionDaoSupport,在spring把sqlSessionFactory创建出来后,会去把之前注入的引用改为真的实例,调用SqlSessionDaoSupport的setSqlSessionFactory方法。

  1. public abstract class SqlSessionDaoSupport extends DaoSupport {
  2. private SqlSession sqlSession;
  3. private boolean externalSqlSession;
  4. public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
  5. if (!this.externalSqlSession) {
  6. this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
  7. }
  8. }
  9. public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
  10. this.sqlSession = sqlSessionTemplate;
  11. this.externalSqlSession = true;
  12. }
  13. /**
  14. * Users should use this method to get a SqlSession to call its statement methods
  15. * This is SqlSession is managed by spring. Users should not commit/rollback/close it
  16. * because it will be automatically done.
  17. *
  18. * @return Spring managed thread safe SqlSession
  19. */
  20. public SqlSession getSqlSession() {
  21. return this.sqlSession;
  22. }
  23. /**
  24. * {@inheritDoc}
  25. */
  26. protected void checkDaoConfig() {
  27. notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
  28. }
  29. }

这里值得注意的是setSqlSessionFactory方法new了一个SqlSessionTemplate。

SqlSessionTemplate

它的一个重要的构造器为

  1. public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
  2. PersistenceExceptionTranslator exceptionTranslator) {
  3. notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  4. notNull(executorType, "Property 'executorType' is required");
  5. this.sqlSessionFactory = sqlSessionFactory;
  6. this.executorType = executorType;
  7. this.exceptionTranslator = exceptionTranslator;
  8. this.sqlSessionProxy = (SqlSession) newProxyInstance(
  9. SqlSessionFactory.class.getClassLoader(),
  10. new Class[] { SqlSession.class },
  11. new SqlSessionInterceptor());
  12. }

mybatis-srping比传统mybatis方法多做的事情就在于此,生成了一个sqlSessionProxy。这里static import了java.lang.reflect.Proxy.newProxyInstance;也就是使用使用jdk代理进行了SqlSessionInterceptor拦截。

SqlSessionInterceptor

  1. private class SqlSessionInterceptor implements InvocationHandler {
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3. SqlSession sqlSession = getSqlSession(
  4. SqlSessionTemplate.this.sqlSessionFactory,
  5. SqlSessionTemplate.this.executorType,
  6. SqlSessionTemplate.this.exceptionTranslator);
  7. try {
  8. Object result = method.invoke(sqlSession, args);
  9. if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
  10. // force commit even on non-dirty sessions because some databases require
  11. // a commit/rollback before calling close()
  12. sqlSession.commit(true);
  13. }
  14. return result;
  15. } catch (Throwable t) {
  16. Throwable unwrapped = unwrapThrowable(t);
  17. if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
  18. // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
  19. closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
  20. sqlSession = null;
  21. Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
  22. if (translated != null) {
  23. unwrapped = translated;
  24. }
  25. }
  26. throw unwrapped;
  27. } finally {
  28. if (sqlSession != null) {
  29. closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
  30. }
  31. }
  32. }
  33. }

到了这里就明白了mybatis-spring帮忙做了session的open和close。

mybatis如何根据mapper接口生成其实现类(springboot)的更多相关文章

  1. mybatis如何根据mapper接口生成其实现类

    SpringBoot集成mybatis mybatis的statement的解析与加载 mybatis如何根据mapper接口生成其实现类 mybatis的mapper返回map结果集 mybatis ...

  2. mybatis中的mapper接口文件以及selectByExample类的实例函数详解

    记录分为两个部分,第一部分主要关注selectByExample类的实例函数的实现:第二部分讨论Mybatis框架下基本的实例函数. (一)selectByExample类的实例函数的实现 当你启动项 ...

  3. mybatis中的mapper接口文件以及example类的实例函数以及详解

    ##Example example = new ##Example(); example.setOrderByClause("字段名 ASC"); //升序排列,desc为降序排列 ...

  4. Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring 非原创[只为记录],原博文地址:https://www.cnblogs.com/ ...

  5. (转)Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring Mybatis在与Spring集成的时候可以配置MapperFactoryBea ...

  6. Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring - 大新博客 - 推酷 - 360安全浏览器 7.1

    Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring - 大新博客 时间 2014-02-11 21:08:00  博客园-所有随笔区 ...

  7. Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring

    上一篇文章我们讲了SqlSessionFactoryBean,通过这个FactoryBean创建SqlSessionFactory并注册进Spring容器,这篇文章我们就讲剩下的部分,通过Mapper ...

  8. idea插件(mybatis框架下mapper接口快速跳转对应xml文件)亲测好用!

    我相信目前在绝大部分公司里,主要使用的框架是S(spring)S(spring MVC)M(mybatis),其中mybatis总体架构是编写mapper接口,框架扫描其对应的mapper.xml文件 ...

  9. MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

    Mybatis在与Spring集成的时候可以配置 MapperFactoryBean来生成Mapper接口的代理. 例如 <bean id="userMapper" clas ...

随机推荐

  1. HDU 4641 K-string 后缀自动机 并查集

    http://acm.hdu.edu.cn/showproblem.php?pid=4641 https://blog.csdn.net/asdfgh0308/article/details/4096 ...

  2. 给HTML初学者的三十条最佳实践

    Nettuts +运营最困难的方面是为很多技能水平不同的用户提供服务.如果我们发布太多高级教程,我的新手用户将无法从中受益.相反也是如此.我们尽我们最大的努力,但如果你觉得你被忽略了请联系我们.这个网 ...

  3. 协议栈中使用crc校验函数

    CRC校验介绍:循环冗余校验码,原理是多项式除法 ZigBee协议栈:能够使zigbee节点相互之间组网,数据传输,数据获取,数据显示 思路以及步骤: 1.因为IAR的程序是用c写的,所以上网查找如何 ...

  4. poj 1330 Nearest Common Ancestors 单次LCA/DFS

    Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 19919   Accept ...

  5. j.u.c系列(01) ---初探ThreadPoolExecutor线程池

    写在前面 之前探索tomcat7启动的过程中,使用了线程池(ThreadPoolExecutor)的技术 public void createExecutor() { internalExecutor ...

  6. Easy WordPress Updates: Store FTP Info in wp-config.php

    Saw an interesting blog post on Twitter today about storing WordPress FTP information in wp-config.p ...

  7. 从 n 个数字中选出 m 个不同的数字,保证这 m 个数字是等概率的

    问题如上. 这是我被面试的一个题目. 我的第一反应给出的解决的方法是.开启  n 个线程并标记序号,各个线程打印出它的序号.直到有 m 个线程被调度时,停止全部线程. 打印出的序号即是 m 个等概率出 ...

  8. HUST 1017 Exact cover(DLX精确覆盖)

    Description There is an N*M matrix with only 0s and 1s, (1 <= N,M <= 1000). An exact cover is ...

  9. jQuery操作字符串

    var str = "我有一头小毛驴,我从来也不骑";   1.打印出某索引位置上的字符 //结果:毛 alert(str.charAt(5));   2.打印出某索引位置上的Un ...

  10. ios的一些知识点

    ios的一些知识点 一 非ARC的内存管理情况 1-autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段落,开 ...