www.91084.com

GVKun编程网logo

Mybatis架构原理(一)(叙述一下mybatis的架构流程)

12

在本文中,我们将详细介绍Mybatis架构原理(一)的各个方面,并为您提供关于叙述一下mybatis的架构流程的相关解答,同时,我们也将为您带来关于2、MyBatis架构原理、java-mybaits

在本文中,我们将详细介绍Mybatis架构原理(一)的各个方面,并为您提供关于叙述一下mybatis的架构流程的相关解答,同时,我们也将为您带来关于2、MyBatis架构原理、java-mybaits-015-mybatis逆向工程最佳实践【基础mybatis-generator、tk.mybatis、mubatis-plus】、java架构之路-(mybatis源码)mybatis基本使用、MyBatis 学习 之 一、MyBatis 简介与配置 MyBatis+Spring+MySql的有用知识。

本文目录一览:

Mybatis架构原理(一)(叙述一下mybatis的架构流程)

Mybatis架构原理(一)(叙述一下mybatis的架构流程)

Mybatis架构原理(一)

架构设计图

总体流程:
  • 加载配置并初始化

  • 触发条件:加载配置文件;配置来源于两个地方,一个是xml文件,一个是java代码中的注解;将主配置文件内容解析封装到configuration,将sql的配置信息加载成为一个mappedstatement对象,存储在内存之中

  • 接受调用请求

  • 触发条件:调用Mybatis提供的API

  • 传入参数:为sql的id和传入参数对象

  • 处理过程:将请求传递给下层的请求处理曾进行处理

  • 处理操作请求

  • 触发条件:API接口层传递请求过来

  • 传入参数为sql的id和传入参数对象

  • 处理过程

  • 根据sql的id查找对应的MappedStatement对象

  • 根据传入参数对象解析MappedStatement对象,得到最终要执行的sql和执行传入参数

  • 获取数据库连接,根据得到的最终sql语句和执行传入参数到数据库执行,并得到执行结果

  • 根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果

  • 释放链接资源

  • 返回处理结果

  • 将最终的处理结果返回


  • Mybatis源码剖析

    mybatis源码下载地址:https://gitee.com/mirrors/mybatis.git

    传统方式的源码分析

    第一步:

    // 1. 读取配置文件,读成字节输入流,注意:现在还没解析 InputStream resourceAsstream = Resources.getResourceAsstream("sqlMapConfig.xml"); ​ -->     public static InputStream getResourceAsstream(ClassLoader loader, String resource) throws IOException {            InputStream in = classLoaderWrapper.getResourceAsstream(resource, loader);            if (in == null) {                throw new IOException("Could not find resource " + resource);           }            return in;       }

    借助类加载器中的getResourceAsstream根据当前配置文件的路径把配置文件加载成输入流进行返回

    第二步:

    // 2. 解析配置文件,封装Configuration对象   创建DefaultsqlSessionFactory对象    sqlSessionFactory sqlSessionFactory = new sqlSessionFactoryBuilder().build(resourceAsstream);     -->    // 调用的重载方法    public sqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {        try {            // 创建 XMLConfigBuilder, XMLConfigBuilder是专门解析mybatis的配置文件的类            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);            // 执行 XML 解析            // 创建 DefaultsqlSessionFactory 对象            return build(parser.parse());       } catch (Exception e) {            throw ExceptionFactory.wrapException("Error building sqlSession.", e);       } finally {            ErrorContext.instance().reset();            try {                inputStream.close();           } catch (IOException e) {                // Intentionally ignore. Prefer prevIoUs error.           }       }   } ​ -->        // 解析 XML 成 Configuration 对象。    public Configuration parse() {        // 若已解析,抛出 BuilderException 异常        if (parsed) {            throw new BuilderException("Each XMLConfigBuilder can only be used once.");       }        // 标记已解析        parsed = true;        ///parser是XPathParser解析器对象,读取节点内数据,<configuration>是MyBatis配置文件中的顶层标签        // 解析 XML configuration 节点        parseConfiguration(parser.evalNode("/configuration"));        return configuration;   }     -->    //解析 XML    private void parseConfiguration(XNode root) {        try {            //issue #117 read properties first            // 解析 <properties /> 标签            propertiesElement(root.evalNode("properties"));            // 解析 <settings /> 标签            Properties settings = settingsAsProperties(root.evalNode("settings"));            // 加载自定义的 VFS 实现类            loadCustomVfs(settings);            // 解析 <typeAliases /> 标签            typeAliasesElement(root.evalNode("typeAliases"));            // 解析 <plugins /> 标签            pluginElement(root.evalNode("plugins"));            // 解析 <objectFactory /> 标签            objectFactoryElement(root.evalNode("objectFactory"));            // 解析 <objectWrapperFactory /> 标签            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));            // 解析 <reflectorFactory /> 标签            reflectorFactoryElement(root.evalNode("reflectorFactory"));            // 赋值 <settings /> 到 Configuration 属性            settingsElement(settings);            // read it after objectFactory and objectWrapperFactory issue #631            // 解析 <environments /> 标签            environmentsElement(root.evalNode("environments"));            // 解析 <databaseIdProvider /> 标签            databaseIdProviderElement(root.evalNode("databaseIdProvider"));            // 解析 <typeHandlers /> 标签            typeHandlerElement(root.evalNode("typeHandlers"));            // 解析 <mappers /> 标签            mapperElement(root.evalNode("mappers"));       } catch (Exception e) {            throw new BuilderException("Error parsing sql Mapper Configuration. Cause: " + e, e);       }   } -->(以properties举例)    //1. 解析 <properties /> 标签,成 Properties 对象。    //2. 覆盖 configuration 中的 Properties 对象到上面的结果。    //3. 设置结果到 parser 和 configuration 中    private void propertiesElement(XNode context) throws Exception {        if (context != null) {            // 读取子标签们,为 Properties 对象            Properties defaults = context.getChildrenAsProperties();            // 读取 resource 和 url 属性            String resource = context.getStringAttribute("resource");            String url = context.getStringAttribute("url");            if (resource != null && url != null) { // resource 和 url 都存在的情况下,抛出 BuilderException 异常                throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");           }            // 读取本地 Properties 配置文件到 defaults 中。            if (resource != null) {                defaults.putAll(Resources.getResourceAsProperties(resource));                // 读取远程 Properties 配置文件到 defaults 中。           } else if (url != null) {                defaults.putAll(Resources.getUrlAsProperties(url));           }            // 覆盖 configuration 中的 Properties 对象到 defaults 中。            Properties vars = configuration.getvariables();            if (vars != null) {                defaults.putAll(vars);           }            // 设置 defaults 到 parser 和 configuration 中。            parser.setvariables(defaults);            configuration.setvariables(defaults);       }   } ​ -->(mapperElement(root.evalNode("mappers"));)    private void mapperElement(XNode parent) throws Exception {        if (parent != null) {            // 遍历子节点            for (XNode child : parent.getChildren()) {                // 如果是 package 标签,则扫描该包                if ("package".equals(child.getName())) {                    // 获取 <package> 节点中的 name 属性                    String mapperPackage = child.getStringAttribute("name");                    // 从指定包中查找 mapper 接口,并根据 mapper 接口解析映射配置                    configuration.addMappers(mapperPackage);                // 如果是 mapper 标签,               } else {                    // 获得 resource、url、class 属性                    String resource = child.getStringAttribute("resource");                    String url = child.getStringAttribute("url");                    String mapperClass = child.getStringAttribute("class"); ​                    // resource 不为空,且其他两者为空,则从指定路径中加载配置                    if (resource != null && url == null && mapperClass == null) {                        ErrorContext.instance().resource(resource);                        // 获得 resource 的 InputStream 对象                        InputStream inputStream = Resources.getResourceAsstream(resource);                        // 创建 XMLMapperBuilder 对象                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getsqlFragments());                        // 执行解析                        mapperParser.parse();                        // url 不为空,且其他两者为空,则通过 url 加载配置                   } else if (resource == null && url != null && mapperClass == null) {                        ErrorContext.instance().resource(url);                        // 获得 url 的 InputStream 对象                        InputStream inputStream = Resources.getUrlAsstream(url);                        // 创建 XMLMapperBuilder 对象                        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getsqlFragments());                        // 执行解析                        mapperParser.parse();                        // mapperClass 不为空,且其他两者为空,则通过 mapperClass 解析映射配置                   } else if (resource == null && url == null && mapperClass != null) {                        // 获得 Mapper 接口                        Class<?> mapperInterface = Resources.classForName(mapperClass);                        // 添加到 configuration 中                        configuration.addMapper(mapperInterface);                        // 以上条件不满足,则抛出异常                   } else {                        throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");                   }               }           }       }   } //加载核心配置文件的时候,根据映射文件进行加载,把映射文件中的每一个标签例如insert,select标签里面的内容封装成一个mappedStatement对象,然后将   mappedStatement封装到configuration中; ​ -->    

    configuration对象重点内容

       /**     * MappedStatement 映射     *     * KEY:`${namespace}.${id}`     */    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection"); //解析映射配置文件,将配置文件里面的每一个标签内容封装成一个mappedStatement对象; ​ -->    /**     * 映射的语句,每个 <select />、<insert />、<update />、<delete /> 对应一个 MappedStatement 对象     */    public final class MappedStatement {... ...}
    执行sql流程

    sqlSession是一个接口,它有两个实现类,DefaultsqlSession(默认)和sqlSessionManager(弃用),

    sqlSession是mybatis中用于和数据库交互的顶层类,通常将它与ThreadLocal绑定,一个会话使用一个sqlSession,由于是线程不安全的,所以使用完毕后需要close;

    Executor
    Executor也是一个接口,他有三个常用的实现类 BatchExecutor (重用语句并执行批量更新) ReuseExecutor (重用预处理语句,prepared statements) SimpleExecutor (普通的执行器,默认)

    第三步:

    // 3. 生产了Defaultsqlsession实例对象   设置了事务不自动提交 完成了executor对象的创建    sqlSession sqlSession = sqlSessionFactory.openSession(); ​ -->       // 进入openSession方法    @Override    public sqlSession openSession() {        //getDefaultExecutorType()传递的是SimpleExecutor        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);   } ​ -->    //进入openSessionFromDataSource。    //ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务    //openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理    private sqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {        Transaction tx = null;        try {            // 获得 Environment 对象            final Environment environment = configuration.getEnvironment();            // 创建 Transaction 对象            final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);            // 创建 Executor 对象            final Executor executor = configuration.newExecutor(tx, execType);            // 创建 DefaultsqlSession 对象            return new DefaultsqlSession(configuration, executor, autoCommit);       } catch (Exception e) {            // 如果发生异常,则关闭 Transaction 对象            closeTransaction(tx); // may have fetched a connection so lets call close()            throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);       } finally {            ErrorContext.instance().reset();       }   }

    第四步:

    // 4.(1)根据statementid来从Configuration中map集合中获取到了指定的MappedStatement对象    //(2)将查询任务委派了executor执行器    User user =  sqlSession.selectList("com.yun.mapper.IUserMapper.findById",1);    System.out.println(user); ​ -->    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {        try {            // 获得 MappedStatement 对象            MappedStatement ms = configuration.getMappedStatement(statement);            // 执行查询            return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);       } catch (Exception e) {            throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);       } finally {            ErrorContext.instance().reset();       }   } ​ -->    //此方法在SimpleExecutor的父类BaseExecutor中实现    @Override    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws sqlException {        //根据传入的参数动态获得sql语句,最后返回用Boundsql对象表示        Boundsql boundsql = ms.getBoundsql(parameter);        //为本次查询创建缓存的Key        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundsql);        // 查询        return query(ms, parameter, rowBounds, resultHandler, key, boundsql);   } ​ -->    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, Boundsql boundsql) throws sqlException {        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());        // 已经关闭,则抛出 ExecutorException 异常        if (closed) {            throw new ExecutorException("Executor was closed.");       }        // 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。        if (queryStack == 0 && ms.isFlushCacherequired()) {            clearLocalCache();       }        List<E> list;        try {            // queryStack + 1            queryStack++;            // 从一级缓存中,获取查询结果            list = resultHandler == null ? (List<E>) localCache.getobject(key) : null;            // 获取到,则进行处理            if (list != null) {                handleLocallyCachedOutputParameters(ms, key, parameter, boundsql);            // 获得不到,则从数据库中查询           } else {                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundsql);           }       } finally {            // queryStack - 1            queryStack--;       }        if (queryStack == 0) {            // 执行延迟加载            for (DeferredLoad deferredLoad : deferredLoads) {                deferredLoad.load();           }            // issue #601            // 清空 deferredLoads            deferredLoads.clear();            // 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {                // issue #482                clearLocalCache();           }       }        return list;   } ​ -->    // 从数据库中读取操作    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, Boundsql boundsql) throws sqlException {        List<E> list;        // 在缓存中,添加占位对象。此处的占位符,和延迟加载有关,可见 `DeferredLoad#canLoad()` 方法        localCache.putObject(key, EXECUTION_PLACEHOLDER);        try {            // 执行读操作            list = doQuery(ms, parameter, rowBounds, resultHandler, boundsql);       } finally {            // 从缓存中,移除占位对象            localCache.removeObject(key);       }        // 添加到缓存中        localCache.putObject(key, list);        // 暂时忽略,存储过程相关        if (ms.getStatementType() == StatementType.CALLABLE) {            localOutputParameterCache.putObject(key, parameter);       }        return list;   } ​ -->    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, Boundsql boundsql) throws sqlException {        Statement stmt = null;        try {            Configuration configuration = ms.getConfiguration();            // 传入参数创建StatementHanlder对象来执行查询            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundsql);            // 创建jdbc中的statement对象            stmt = prepareStatement(handler, ms.getStatementLog());            // 执行 StatementHandler ,进行读操作            return handler.query(stmt, resultHandler);       } finally {            // 关闭 StatementHandler 对象            closeStatement(stmt);       }   } ​ -->     // 初始化 StatementHandler 对象    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws sqlException {        Statement stmt;        // 获得 Connection 对象        Connection connection = getConnection(statementLog);        // 创建 Statement 或 PrepareStatement 对象        stmt = handler.prepare(connection, transaction.getTimeout());        // 设置 sql 上的参数,例如 PrepareStatement 对象上的占位符        handler.parameterize(stmt);        return stmt;   }

    第五步:

    // 5.释放资源    sqlSession.close();

    mapper代理方式源码分析,前三步和传统方式相同,这边不在叙述

    第四步

    思考一个问题,通常mapper接口我们都没有实现的方法却可以使用,是为什么呢?答案很简单:  动态代理 

    开始之前介绍一下Mybatis初始化时对接口的处理,MapperRegistry是Configuration中的一个属性,它内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类,mappers可以配置接口的包路径,或者某个具体的接口类

    <mappers> <mapper/> <package name = "com.yun.mapper"/> </mappers>

    当解析mappers标签时,它会判断解析到的是mapper配置文件时,会再将对应配置文件中的CRUD标签一一封装成MappedStatement对象,存入mappedStatements中,当判断解析到接口时,会建此接口对应的MapperProxyFactory对象,存入HashMap中,key = 接口的字节码对象,value = 此接口对应的MapperProxyFactory对象;

    // 使用JDK动态代理对mapper接口产生代理对象    IUserMapper mapper = sqlSession.getMapper(IUserMapper.class); ​ -->    //这个类中维护一个HashMap存放MapperProxyFactory    private final Map<Class<?>, MapperProxyFactory<?>> kNownMappers = new HashMap<>();        public <T> T getMapper(Class<T> type, sqlSession sqlSession) {        // 获得 MapperProxyFactory 对象        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) kNownMappers.get(type);        // 不存在,则抛出 BindingException 异常        if (mapperProxyFactory == null) {            throw new BindingException("Type " + type + " is not kNown to the MapperRegistry.");       }        /// 通过动态代理工厂生成实例。        try {            return mapperProxyFactory.newInstance(sqlSession);       } catch (Exception e) {            throw new BindingException("Error getting mapper instance. Cause: " + e, e);       }   } ​ -->    //MapperProxyFactory类中的newInstance方法 MapperProxy实现了InvocationHandler接口    public T newInstance(sqlSession sqlSession) {        // 创建了JDK动态代理的invocationHandler接口的实现类mapperProxy        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);        // 调用了重载方法        return newInstance(mapperProxy);   }        protected T newInstance(MapperProxy<T> mapperProxy) {        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);   } ​ 当代理对象去调用接口的任意方法时,都要去执行动态代理中MapperProxy里面的invoke方法, ​ -->       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        try {            // 如果是 Object 定义的方法,直接调用            if (Object.class.equals(method.getDeclaringClass())) {                return method.invoke(this, args); ​           } else if (isDefaultMethod(method)) {                return invokeDefaultMethod(proxy, method, args);           }       } catch (Throwable t) {            throw ExceptionUtil.unwrapThrowable(t);       }        // 获得 MapperMethod 对象        final MapperMethod mapperMethod = cachedMapperMethod(method);        // 重点在这:MapperMethod最终调用了执行的方法        return mapperMethod.execute(sqlSession, args);   } ​ -->    public Object execute(sqlSession sqlSession, Object[] args) {        Object result;        //判断mapper中的方法类型,最终调用的还是sqlSession中的方法        switch (command.getType()) {            case INSERT: {                // 转换参数                Object param = method.convertArgsTosqlCommandParam(args);                // 执行 INSERT 操作                // 转换 rowCount                result = rowCountResult(sqlSession.insert(command.getName(), param));                break;           }            case UPDATE: {                // 转换参数                Object param = method.convertArgsTosqlCommandParam(args);                // 转换 rowCount                result = rowCountResult(sqlSession.update(command.getName(), param));                break;           }            case DELETE: {                // 转换参数                Object param = method.convertArgsTosqlCommandParam(args);                // 转换 rowCount                result = rowCountResult(sqlSession.delete(command.getName(), param));                break;           }            case SELECT:                // 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理                if (method.returnsVoid() && method.hasResultHandler()) {                    executeWithResultHandler(sqlSession, args);                    result = null;                // 执行查询,返回列表               } else if (method.returnsMany()) {                    result = executeForMany(sqlSession, args);                // 执行查询,返回 Map               } else if (method.returnsMap()) {                    result = executeForMap(sqlSession, args);                // 执行查询,返回 Cursor               } else if (method.returnsCursor()) {                    result = executeForCursor(sqlSession, args);                // 执行查询,返回单个对象               } else {                    // 转换参数                    Object param = method.convertArgsTosqlCommandParam(args);                    // 查询单条                    result = sqlSession.selectOne(command.getName(), param);                    if (method.returnsOptional() &&                           (result == null || !method.getReturnType().equals(result.getClass()))) {                        result = Optional.ofNullable(result);                   }               }                break;            case FLUSH:                result = sqlSession.flushStatements();                break;            default:                throw new BindingException("UnkNown execution method for: " + command.getName());       }        // 返回结果为 null ,并且返回类型为基本类型,则抛出 BindingException 异常        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {            throw new BindingException("Mapper method '" + command.getName()                    + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");       }        // 返回结果        return result;   }      

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    2、MyBatis架构原理

    2、MyBatis架构原理

    一、架构设计

    image-20211029140627246

    Mybatis的功能架构分为三层:

    • API接⼝层:提供给外部使⽤的接⼝ API,开发⼈员通过这些本地API来操纵数据库。接⼝层⼀接收 到 调⽤请求就会调⽤数据处理层来完成具体的数据处理。

      MyBatis和数据库的交互有两种⽅式:

      • 使⽤传统的MyBati s提供的API ;

      • 使⽤Mapper代理的⽅式

    • 数据处理层:负责具体的SQL查找、SQL解析、SQL执⾏和执⾏结果映射处理等。它主要的⽬的是根 据调⽤的请求完成⼀次数据库操作。

    • 基础⽀撑层:负责最基础的功能⽀撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是 共 ⽤的东⻄,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的⽀撑

    二、主要构件及其相互关系

    • SqlSession: MyBatis⼯作的主要顶层API,表示和数据库交互的会话,完成必要数 据库增删改查功能
    • Executor:MyBatis执⾏器,是MyBatis调度的核⼼,负责SQL语句的⽣成和查询缓 存的维护
    • StatementHandler:封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参 数、将Statement结果集转换成List集合
    • ParameterHandler:负责对⽤户传递的参数转换成JDBC Statement所需要的参数
    • ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
    • TypeHandler:负责java数据类型和jdbc数据类型之间的映射和转换
    • MappedStatement:MappedStatement维护了⼀条<select | update | delete | insert>节点 的封 装
    • SqlSource:负责根据⽤户传递的parameterObject,动态地⽣成SQL语句,将信息封 装到BoundSql对象中,并返回
    • BoundSql:表示动态⽣成的SQL语句以及相应的参数信息

    Mybatis层次结构

    java-mybaits-015-mybatis逆向工程最佳实践【基础mybatis-generator、tk.mybatis、mubatis-plus】

    java-mybaits-015-mybatis逆向工程最佳实践【基础mybatis-generator、tk.mybatis、mubatis-plus】

    一、概述

    三款框架的功能对比

     

    Mybatis-generator

    通用Mapper

    Mybatis-Plus

    代码生成器

    支持自动生成Model,Mapper,Mapper XML文件

    生成方式不够灵活;

    生成代码功能较为简单

    支持自动生成Entity,Mapper,Mapper XML文件;

    提供通用的Mapper模板,生成方式较灵活;

    生成的Model文件包含注释能够很好地与数据库表完成映射

    支持自动生成Entity,Mapper,Mapper XML,Service,Controller文件;

    提供BaseMapper接口

    CRUD操作

    代码生成后每个Mapper有固定的CRUD方法;

    在每个Mapper上分别扩展

    提供通用Mapper接口;方便构造统一service

    可以扩展通用接口

    提供BaseMapper接口;

    可以扩展通用接口

    条件构造器

    每个实体类自己的Example构造条件【对象方式】

    提供通用Example【构建sql】

    提供Wrapper进行复杂条件构造

    乐观锁

     

    支持

    支持

    主键策略

     

    支持

    支持

    分页

     

     

    支持

    逻辑删除

     

     

    支持

    通用枚举

     

     

    支持

    攻击Sql阻断

     

     

    支持

    性能分析

     

     

    支持

      通用Mapper是对Mybatis-generator的升级改造,解决了使用Mybatis-generator可能需要大量重构的问题,并且在这个基础上加入了一些新的功能。

      Mybatis-Plus可以看作是在另一个方向上对Mybatis的升级改造,不仅能够根据数据库表快速生成pojo实体类,还封装了大量CRUD方法,使用Wrapper解决了复杂条件构造等问题,更是根据开发中常见的问题给出了一系列解决方案。

      在拥有Maven和Spring boot的开发框架下,MBG、通用Mapper和MP都可以快速地完成安装,相比于MBG和通用Mapper仅需要执行插件就可以完成基本的开发工作,MP可能需要更多的开发工作量。

      最佳实践参看代码:https://github.com/bjlhx15/java_base_architecture.git

    二、Mybatis-generator

      MyBatis Generator(MBG)是MyBatis MyBatis 和iBATIS的代码生成器。它为所有版本的MyBatis以及版本2.2.0之后的iBATIS版本生成代码。它将根据数据库表生成可用于访问表的文件。

      Mybatis-generator主要完成的工作是依据数据库表创建对应的model、dao、mapping文件,可以通过Maven插件或者mybatis-generator的jar包生成。

      这里介绍Maven插件使用方法:

        1、在pom.xml文件中加入mybatis-generator系列插件;

        2、创建generatorConfig.xml文件,在配置文件中指定数据库连接地址、生成类的存放地址、生成对应表的类名等信息;

        3、执行mybatis-generator插件,即可生成相应文件;

        4、此外,mybatis-generator自动生成了example类用于构造复杂的筛选条件

      Mybatis-generator使用较为简单,生成的DAO类及映射文件中包含基本的CRUD操作。

      需要注意的是,在一次项目中多次执行mybatis-generator,xml文件会追加,会覆盖原本的Model、DAO;可以使用如下方式

        

        1、每次清理要生成的xml

        2、自动生成的写入auto,

        3、个人扩展的使用上图方式

      可以参看上述github代码

    三、通用Mapper【tk.mybatis】

    官网:https://github.com/abel533/Mapper/wiki

      当数据库字段变化频繁时,使用MBG(mybatis-generator)会带来大量的重构工作,对此,通用Mapper给出的解决办法是:给予开发者一个具备丰富的单表方法并且容易扩展的通用的Mapper。

      通用Mapper是对单表的CRUD操作进行了较为详细的实现,使得开发人员可以随意的按照自己的需求选择通用的方法,同时允许开发人员便捷地对通用Mapper进行扩展。

    1. 在pom.xml中添加插件配置

    <!-- 命令:mvn mybatis-generator:generate -->
                <plugin>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-maven-plugin</artifactId>
                    <version>1.3.6</version>
                    <configuration>
                        <!-- 配置实际调用地址-->
                        <configurationFile>src/main/resources/mybatis_generatorConfig/generatorConfig-base.xml
                        </configurationFile>
                        <verbose>true</verbose>
                        <overwrite>true</overwrite>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>mysql</groupId>
                            <artifactId>mysql-connector-java</artifactId>
                            <version>5.1.38</version>
                        </dependency>
                        <dependency>
                            <groupId>tk.mybatis</groupId>
                            <artifactId>mapper</artifactId>
                            <version>4.0.0</version>
                        </dependency>
                    </dependencies>
                </plugin>
    View Code

      可以看到,通用Mapper的代码生成实际上是使用了MGB,因此通用Mapper的代码生成器只是调用了MGB,然后在这个基础上加入了一些元素来方便开发。

    2. 配置文件generatorConfig.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE generatorConfiguration PUBLIC
            "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
    <generatorConfiguration>
        <!--导入属性配置,前面我们写的一个配置文件,你也可以直接使用mybatis的jdbc的配置文件 -->
        <properties resource="jdbc.properties"></properties>
        <!-- 数据库驱动,注意,这里必须要修改成你的数据库的驱动地址 -->
        <!--    如果 pom配置这里可以不写-->
        <!--    <classPathEntry  location=".m2/repository/mysql/mysql-connector-java/5.1.8/mysql-connector-java-5.1.8.jar"/>-->
    
        <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
            <property name="beginningDelimiter" value="`"/>
            <property name="endingDelimiter" value="`"/>
            <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
                <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
                <property name="caseSensitive" value="true"/>
            </plugin>
    
            <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
            <jdbcConnection driverClass="${jdbc.driver}"
                            connectionURL="${jdbc.url}"
                            userId="${jdbc.username}" password="${jdbc.password}">
            </jdbcConnection>
    
    <!--        <javaTypeResolver>-->
    <!--            <property name="forceBigDecimals" value="false"/>-->
    <!--        </javaTypeResolver>-->
            <!-- targetProject:生成PO类的位置 -->
            <javaModelGenerator targetPackage="com.github.bjlhx15.mybatis.springboot.base.model.auto"
                                targetProject="../tk-mybatis-springboot-base1/src/main/java">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false"/>
                <!-- 从数据库返回的值被清理前后的空格 -->
                <property name="trimStrings" value="true"/>
            </javaModelGenerator>
    
            <!-- targetProject:mapper xml 映射文件生成的位置 -->
            <sqlMapGenerator targetPackage="autoxml"
                             targetProject="../tk-mybatis-springboot-base1/src/main/resources/mapper/">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false"/>
            </sqlMapGenerator>
    
            <!--targetPackage:mapper接口生成的位置 -->
            <javaClientGenerator type="XMLMAPPER"
                                 targetPackage="com.github.bjlhx15.mybatis.springboot.base.repository.auto"
                                 targetProject="../tk-mybatis-springboot-base1/src/main/java">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false"/>
            </javaClientGenerator>
    
            <!-- 指定数据库表 -->
            <table tableName="accountbalance" domainObjectName="AccountBalance">
                <generatedKey column="id" sqlStatement="JDBC"/>
            </table>
    
    
        </context>
    </generatorConfiguration>
    View Code

      与MGB原有的配置文件相比,这里只是多了一个插件的配置,这个插件的作用是:配置生成的Mapper接口都会自动继承这个接口,也就是说,使用通用Mapper生成的mapper接口即使看上去空空如也,但已经具备了大量的对数据库的操作方法。此外,通用 Mapper 专用代码生成器生成的 Model 会在原有基础上增加 @Table,@Id,@Column 等注解,方便自动与数据库字段进行映射。

    3. 在 pom.xml 这一级目录的命令行窗口执行 mvn mybatis-generator:generate或者直接在执行mvn插件即可。

    4. 通用Mapper同样有Example的设计,与MGB不同的是,MDB会对每一个表生成对应的Example类,而通用Mapper提供了一个统一的Example类,这个类和 MBG 生成的相比,需要自己设置属性名,这个类还额外提供了更多的方法。

      通用Mapper可以看作是MGB的改进和扩展,一定程度上解决了使用MGB重构的问题。

      可以参看上述github代码

    四、Mybatis-Plus

      官网地址:https://mp.baomidou.com/guide/generator.html

      (以下简称MP)是Mybatis的增强工具(MBG和通用Mapper可看成插件),在Mybatis的基础上增加了很多功能,简化开发,提高效率。

    在Spring Boot中的引入:

    1. 在maven中添加MP启动器

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.1.2</version>
    </dependency>

    2. 要使用代码生成器还需要添加模板引擎依赖

    参看地址:AutoGenerator的使用参考 https://mybatis.plus/guide/generator.html 。

    添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。

    Velocity(默认):

    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.1</version>
    </dependency>

    Freemarker:

    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.29</version>
    </dependency>

    Beetl:

    <dependency>
        <groupId>com.ibeetl</groupId>
        <artifactId>beetl</artifactId>
        <version>3.0.11.RELEASE</version>
    </dependency>

    注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。

    AutoGenerator generator = new AutoGenerator();
    
    // set freemarker engine
    generator.setTemplateEngine(new FreemarkerTemplateEngine());
    
    // set beetl engine
    generator.setTemplateEngine(new BeetlTemplateEngine());
    
    // set custom engine (reference class is your custom engine class)
    generator.setTemplateEngine(new CustomTemplateEngine());
    
    // other config
    ...
    View Code

    3、代码的自动生成。AutoGenerator是MP的代码生成器,通过调用AutoGenerator,进行相应的模板、策略配置可以快速生成Entity、Mapper、Mapper XML、Service、Controller各个模块的代码。

    4. MP将通用的CRUD操作封装进BaseMapper接口,而自动生成的Mapper接口便自动继承了BaseMapper接口。复杂的Sql操作,则可以使用QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper)进行动态Sql拼接。

    5、此外,MP还支持分页、逻辑删除、通用枚举、Sql注入等功能,在:https://mp.baomidou.com/guide/generator.html

      还有性能分析插件:https://mp.baomidou.com/guide/performance-analysis-plugin.html

    与MBG不同,使用Mybatis-Plus自动生成代码需要编写代码,通过调用AutoAutoGenerator类实现代码生成,从这方面来说不如使用插件方便。但是它丰富的功能以及只是相对复杂的配置还是使它领先于MBG以及通用Mapper。

     

     

     

     

     

     

     

     

    双方都

    java架构之路-(mybatis源码)mybatis基本使用

    java架构之路-(mybatis源码)mybatis基本使用

      我们今天先来简单了解一下我们持久层框架,mybatis的使用。而且现在的注解成为趋势,我主要说一下注解方向的使用吧(配置文件也会说)

    从使用角度只要是三个部分,mybatis-config.xml,mapper.xml,执行文件三个部分。

    mybatis-config.xml:

      主键标签为configuration成对出现的,然后是properties也就是我们的配置,用于配置数据库。settings声明一些配置,比如打印sql语句等,后面会一个个去说。然后就是我们的mappers,里面包含多个mapper标签,也就是对应我们的mapper.xml文件,在这里说一下一共有三种注入的方式,resource,class,url,resource是通过classpath配置的,如果你没有把mapper放置在resources配置下面,需要在maven里设置编译,不然我们的mapper.xml不会编译到classpath里,class通过类来注入mapper,url一般是远程注入的。再就是我们的typehandlers,可以指定类型转换的。我们也可以继承BaseTypeHandler来重写父类的方法来自定义类型转换。

    来一个我自己的简单配置。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <properties resource="app.properties">
            <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
        </properties>
    
        <settings>
            <!-- 打印查询语句 -->
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
    
        <environments default="${default.environment}">
            <environment id="dev">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper resource="mybatis/dao/StudentMapper.xml"/>
            <!--        <mapper></mapper>-->
            <!--        <mapper url="mybatis.dao.StudentDao"></mapper>-->
        </mappers>
    
    </configuration>

    配置还要很多,后面源码解析里面会一点点来说明。

    mapper.xml:

      这个文件就是我们的编写sql的文件了,里面主要标签就是select,insert,update,delete我们的增删改查标签,再就是我们的缓存设置(二级缓存)。下次博客主要说博客,源码级的。
    select里包含我们常见的id,resultType,resultMap,id用来指向我们的接口文件的类名,resultType为我们mybatis自带的类型,也可以是我们设置的对象Bean,resultMap是我们自己定义的返回类型。这里可能会有疑问,一堆多应该怎么来配置?
    association我们可以用他来指定一对多的配置,同时可以配置懒查询还是及时查询的。我们来看一个实例,我们现有学生表格分数表,学生对应很多科目的分数,我们来看一下。先来一段测试代码。
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost/jdbc"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
    <!--        <mapper resource="mybatis/dao/StudentMapper.xml"/>-->
            <mapper class="mybatis.dao.StudentMapper"></mapper>
            <mapper class="mybatis.dao.ScoreMapper"></mapper>
        </mappers>
    </configuration>
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="mybatis.dao.StudentMapper">
        <select id="selectUser" resultType="mybatis.bean.StudentBean">
            select * from stu where id = #{id}
        </select>
    </mapper>
    package mybatis.bean;
    
    import java.io.Serializable;
    
    public class StudentBean implements Serializable {
    
        private static final long serialVersionUID = 2193971369901198487L;
        private int id;
        private String name;
        
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
    
    
    
        @Override
        public String toString() {
            return "selectUser{" +
                    "id=" + id +
                    "name=" + name +
                    ''}'';
        }
    }
    package mybatis;
    
    import mybatis.bean.ScoreBean;
    import mybatis.bean.StudentBean;
    import mybatis.dao.ScoreMapper;
    import mybatis.dao.StudentMapper;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    
    public class Test1 {
    
    
        @Test
        public void test() throws IOException {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            SqlSession session = sqlSessionFactory.openSession();
            StudentMapper mapper = session.getMapper(StudentMapper.class);
            StudentBean result = mapper.selectUser(1);
    
            System.out.println(result);
        }
    }

    这样我们查询到我们的学生信息,但是还未包含我们的分数。我们来改造一下。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="mybatis.dao.StudentMapper">
        <resultMap id="studentMap" type="mybatis.bean.StudentBean">
            <id property="id" column="id"></id>
            <collection property="scoreBean" ofType="mybatis.bean.ScoreBean">
                <id property="id" column="sid"></id>
    <!--            <result property="subject" column="subject"></result>-->
                <result property="score" column="score"></result>
                <result property="studentId" column="studentId"></result>
            </collection>
    
        </resultMap>
    
        <select id="selectUser" resultMap="studentMap">
            select t.id,t.name,t.address,t.num,s.id as sid,s.subject,s.score,s.studentId as studentId from student t left join score s on s.studentId = t.id where t.id = #{id}
        </select>
    </mapper>

    这样就可以查询到对应关系了。需要注意的事子表如果和主表重名,一定给子表起一个别名,而且子表的每一项需要写result,不然没有结果的,但是还不是很好,本来是一个对象一个集合,现在直接变成集合了,我们再来改改。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="mybatis.dao.StudentMapper">
        <resultMap id="studentMap" type="mybatis.bean.StudentBean">
            <id property="id" column="id"></id>
            <collection property="scoreBean" javaType="java.util.ArrayList" ofType="mybatis.bean.ScoreBean" select="mybatis.dao.ScoreMapper.selectScoreByStudentId" column="{studentId=id}">
            </collection>
        </resultMap>
    
        <select id="selectUser" resultMap="studentMap">
            select * from student t where t.id = #{id}
        </select>
    </mapper>

    这个比较好用,但是切记,不是懒加载,不是懒加载,需要注意的是collection的property指定我们实体类Bean类中集合的名字。ofType是指向我们一对多中多方的实体Bean,select指定我们对应的第二句sql语句,也就是我们的子查询语句。

    column="{studentId=id}" 中studentId是我们的子查询参数名,id是和我们主表的对应关系。就是说,我们要子表的什么参数等于我们的主表的哪个参数传递过去。

     

    接下来就是我们简单的一对一了(也可以当做一对多,但是没啥卵用的多对一,项目经理让从多往一查的时候,请你吐他。。。)我们来看我一下我的配置

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="mybatis.dao.ScoreMapper">
        <resultMap id="scoreMap" type="mybatis.bean.ScoreBean">
            <id property="id" column="id"></id>
            <result property="subject" column="subject"></result>
            <result property="score" column="score"></result>
    
            <association property="studentBean" javaType="mybatis.bean.StudentBean" column="studentId">
                <id property="id" column="id"></id>
                <result property="name" column="name"></result>
                <result property="address" column="address"></result>
                <result property="num" column="num"></result>
            </association>
        </resultMap>
    
        <select id="selectScoreByStudentId" resultMap="scoreMap">
            select * from score s left join student t on s.studentId = t.id where studentId = #{studentId}
        </select>
    </mapper>

    这个比较简单,就不说了,就是写了一个resultMap。我们来看一下两条sql的怎么来进行。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="mybatis.dao.ScoreMapper">
        <resultMap id="scoreMap" type="mybatis.bean.ScoreBean">
            <id property="id" column="id"></id>
            <result property="subject" column="subject"></result>
            <result property="score" column="score"></result>
    
            <association property="studentBean" javaType="mybatis.bean.StudentBean" select="mybatis.dao.StudentMapper.selectUser" column="studentId">
                <id property="id" column="id"></id>
                <result property="name" column="name"></result>
                <result property="address" column="address"></result>
                <result property="num" column="num"></result>
            </association>
        </resultMap>
    
        <select id="selectScoreByStudentId" resultMap="scoreMap">
            select * from score where studentId = #{studentId}
        </select>
    </mapper>

    简单解释一下,其实和上面collection差不多的,select指定查询sql位置,column执行传递过去的参数。

     

    其余的insert,update,delete都差不多,我这里就放在一起说了。

    id 命名空间中的唯一标识符,可被用来代表这条语句。
    parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。
    parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
    flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。
    timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
    statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
    useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
    keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset)。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
    keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望使用多个生成的列,也可以设置为逗号分隔的属性名称列表。
    databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。

     useGeneratedKeys=”true”这个相当来说配置的还是比较多的,也就是我们新增成功后,我们的对象可以返回我们插入成功的主键ID。

    拼装sql:

      #{}:是预处理,也是一个占位符的方式来执行sql,${}是sql的拼接,我们其实可以这样来写。

      @Select("select * from ${}_sys_log where id=#{condition}")
        public SystemLog findSystemLog(String year,String condition);

    也就是说,我们对日志sys_log表做了分库分表,按照年份来区分的表,这时我们可以采用sql拼接的方式来做。

    但尽力不要用拼接的方式来做,后面我将动态sql会说具体怎么来实现。${}容易被sql注入。所以我们尽力还用占位符的方式来处理我们的SQL。

    然后就是我们的插件集成,还有缓存,下次博客我们来说说缓存吧。 

     

    忘记了,学mybatis的使用,推荐一个网站https://mybatis.org/mybatis-3/zh/index.html  上面挺全的(没有源码解析,源码还得回来看我博客)。

     

    最进弄了一个公众号,小菜技术,欢迎大家的加入

    原文出处:https://www.cnblogs.com/cxiaocai/p/11520734.html

    MyBatis 学习 之 一、MyBatis 简介与配置 MyBatis+Spring+MySql

    MyBatis 学习 之 一、MyBatis 简介与配置 MyBatis+Spring+MySql

    一、MyBatis 简介与配置 MyBatis+Spring+MySql

    MyBatis 学习 之 一、MyBatis 简介与配置 MyBatis+Spring+MySql

    MyBatis 学习 之 二、SQL 语句映射文件 (1) resultMap

    MyBatis 学习 之 二、SQL 语句映射文件 (2) 增删改查、参数、缓存

    MyBatis 学习 之 三、动态 SQL 语句

    MyBatis 学习 之 四、MyBatis 配置文件

    1.1MyBatis 简介

          MyBatis 是一个可以自定义 SQL、存储过程和高级映射的持久层框架。MyBatis 摒除了大部分的 JDBC 代码、手工设置参数和结果集重获。MyBatis 只使用简单的 XML 和注解来配置和映射基本数据类型、Map 接口和 POJO 到数据库记录。相对 Hibernate 和 Apache OJB 等 “一站式” ORM 解决方案而言,Mybatis 是一种 “半自动化” 的 ORM 实现。
    需要使用的 Jar 包:mybatis-3.0.2.jar(mybatis 核心包)。mybatis-spring-1.0.0.jar (与 Spring 结合包)。

    下载地址:
    http://ibatis.apache.org/tools/ibator
    http://code.google.com/p/mybatis/

     

    1.2MyBatis+Spring+MySql 简单配置

    1.2.1 搭建 Spring 环境

    1, 建立 maven 的 web 项目;
    2, 加入 Spring 框架、配置文件;
    3, 在 pom.xml 中加入所需要的 jar 包(spring 框架的、mybatis、mybatis-spring、junit 等);
    4, 更改 web.xml 和 spring 的配置文件;
    5, 添加一个 jsp 页面和对应的 Controller;
    6, 测试。

    可参照:http://limingnihao.iteye.com/blog/830409。使用 Eclipse 的 Maven 构建 SpringMVC 项目


    1.2.2 建立 MySql 数据库

    建立一个学生选课管理数据库。
    表:学生表、班级表、教师表、课程表、学生选课表。
    逻辑关系:每个学生有一个班级;每个班级对应一个班主任教师;每个教师只能当一个班的班主任;

    使用下面的 sql 进行建数据库,先建立学生表,插入数据(2 条以上)。

    更多 sql 请下载项目源文件,在 resource/sql 中。

    /* 建立数据库 */
    CREATE DATABASE STUDENT_MANAGER;
    USE STUDENT_MANAGER;
    
    /***** 建立student表 *****/
    CREATE TABLE STUDENT_TBL
    (
       STUDENT_ID         VARCHAR(255) PRIMARY KEY,
       STUDENT_NAME       VARCHAR(10) NOT NULL,
       STUDENT_SEX        VARCHAR(10),
       STUDENT_BIRTHDAY   DATE,
       CLASS_ID           VARCHAR(255)
    );
    
    /*插入学生数据*/
    INSERT INTO STUDENT_TBL (STUDENT_ID,
                             STUDENT_NAME,
                             STUDENT_SEX,
                             STUDENT_BIRTHDAY,
                             CLASS_ID)
      VALUES   (123456,
                ''某某某'',
                ''女'',
                ''1980-08-01'',
                121546
                )


    创建连接 MySql 使用的配置文件 mysql.properties。

    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/student_manager?user=root&password=limingnihao&useUnicode=true&characterEncoding=UTF-8

     

    1.2.3 搭建 MyBatis 环境

    顺序随便,现在的顺序是因为可以尽量的少的修改写好的文件。


    1.2.3.1 创建实体类: StudentEntity

    public class StudentEntity implements Serializable {
    
    	private static final long serialVersionUID = 3096154202413606831L;
    	private ClassEntity classEntity;
    	private Date studentBirthday;
    	private String studentID;
    	private String studentName;
    	private String studentSex;
    	
    	public ClassEntity getClassEntity() {
    		return classEntity;
    	}
    
    	public Date getStudentBirthday() {
    		return studentBirthday;
    	}
    
    	public String getStudentID() {
    		return studentID;
    	}
    
    	public String getStudentName() {
    		return studentName;
    	}
    
    	public String getStudentSex() {
    		return studentSex;
    	}
    
    	public void setClassEntity(ClassEntity classEntity) {
    		this.classEntity = classEntity;
    	}
    
    	public void setStudentBirthday(Date studentBirthday) {
    		this.studentBirthday = studentBirthday;
    	}
    
    	public void setStudentID(String studentID) {
    		this.studentID = studentID;
    	}
    
    	public void setStudentName(String studentName) {
    		this.studentName = studentName;
    	}
    
    	public void setStudentSex(String studentSex) {
    		this.studentSex = studentSex;
    	}
    }

     

    1.2.3.2 创建数据访问接口

    Student 类对应的 dao 接口:StudentMapper。

    public interface StudentMapper {
    	
    	public StudentEntity getStudent(String studentID);
    	
    	public StudentEntity getStudentAndClass(String studentID);
    	
    	public List<StudentEntity> getStudentAll();
    	
    	public void insertStudent(StudentEntity entity);
    	
    	public void deleteStudent(StudentEntity entity);
    	
    	public void updateStudent(StudentEntity entity);
    }


    1.2.3.3 创建 SQL 映射语句文件


    Student 类的 sql 语句文件 StudentMapper.xml
    resultMap 标签:表字段与属性的映射。
    Select 标签:查询 sql。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.manager.data.StudentMapper">
    
    	<resultMap type="StudentEntity" id="studentResultMap">
    		<id property="studentID" column="STUDENT_ID"/>
    		<result property="studentName" column="STUDENT_NAME"/>
    		<result property="studentSex" column="STUDENT_SEX"/>
    		<result property="studentBirthday" column="STUDENT_BIRTHDAY"/>
    	</resultMap>
    	
    	<!-- 查询学生,根据id -->
    	<select id="getStudent" parameterType="String" resultType="StudentEntity" resultMap="studentResultMap">
    		<![CDATA[
    			SELECT * from STUDENT_TBL ST
    				WHERE ST.STUDENT_ID = #{studentID} 
    		]]> 
    	</select>
    	
    	<!-- 查询学生列表 -->
    	<select id="getStudentAll"  resultType="com.manager.data.model.StudentEntity" resultMap="studentResultMap">
    		<![CDATA[
    			SELECT * from STUDENT_TBL
    		]]> 
    	</select>
    	
    </mapper>


    1.2.3.4 创建 MyBatis 的 mapper 配置文件

    在 src/main/resource 中创建 MyBatis 配置文件:mybatis-config.xml。
    typeAliases 标签:给类起一个别名。com.manager.data.model.StudentEntity 类,可以使用 StudentEntity 代替。
    Mappers 标签:加载 MyBatis 中实体类的 SQL 映射语句文件。

     

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    	<typeAliases>
    		<typeAlias alias="StudentEntity" type="com.manager.data.model.StudentEntity"/>
    	</typeAliases>
    	<mappers>
    		<mapper resource="com/manager/data/maps/StudentMapper.xml" />
    	</mappers>
    </configuration>


    1.2.3.5 修改 Spring 的配置文件

    主要是添加 SqlSession 的制作工厂类的 bean:SqlSessionFactoryBean,(在 mybatis.spring 包中)。需要指定配置文件位置和 dataSource。
    和数据访问接口对应的实现 bean。通过 MapperFactoryBean 创建出来。需要执行接口类全称和 SqlSession 工厂 bean 的引用。

    <!-- 导入属性配置文件 -->
    <context:property-placeholder location="classpath:mysql.properties" />
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    	<property name="driverClassName" value="${jdbc.driverClassName}" />
    	<property name="url" value="${jdbc.url}" />
    </bean>
    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="dataSource" />
    </bean>
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    	<property name="configLocation" value="classpath:mybatis-config.xml" />
    	<property name="dataSource" ref="dataSource" />
    </bean>
    
    <!— mapper bean -->
    <bean id="studentMapper" class="org.mybatis.spring.MapperFactoryBean">
    	<property name="mapperInterface" value="com.manager.data.StudentMapper" />
    	<property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>

    也可以不定义 mapper 的 bean,使用注解:

    将 StudentMapper 加入注解

    @Repository
    @Transactional
    public interface StudentMapper {
    }

    对应的需要在 dispatcher-servlet.xml 中加入扫描:

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
            <property name="basePackage" value="com.liming.manager"/>
            <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
        </bean>

     

    1.2.4 测试 StudentMapper

    使用 SpringMVC 测试,创建一个 TestController,配置 tomcat,访问 index.do 页面进行测试:

    @Controller
    public class TestController {
    
    	@Autowired
    	private StudentMapper studentMapper;
    	
    	@RequestMapping(value = "index.do")
    	public void indexPage() {	
    		StudentEntity entity = studentMapper.getStudent("10000013");
    		System.out.println("name:" + entity.getStudentName());
    	}	
    }

     

    使用 Junit 测试:

    使用Junit测试:
    Java代码
    @RunWith(value = SpringJUnit4ClassRunner.class)
    @ContextConfiguration(value = "test-servlet.xml")
    public class StudentMapperTest {
    	
    	@Autowired
    	private ClassMapper classMapper;
    	
    	@Autowired
    	private StudentMapper studentMapper;
    	
    	@Transactional
    	public void getStudentTest(){
    		StudentEntity entity = studentMapper.getStudent("10000013");
    		System.out.println("" + entity.getStudentID() + entity.getStudentName());
    		
    		List<StudentEntity> studentList = studentMapper.getStudentAll();
    		for( StudentEntity entityTemp : studentList){
    			System.out.println(entityTemp.getStudentName());
    		}
    		
    	}
    }

     

    关于Mybatis架构原理(一)叙述一下mybatis的架构流程的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于2、MyBatis架构原理、java-mybaits-015-mybatis逆向工程最佳实践【基础mybatis-generator、tk.mybatis、mubatis-plus】、java架构之路-(mybatis源码)mybatis基本使用、MyBatis 学习 之 一、MyBatis 简介与配置 MyBatis+Spring+MySql的相关知识,请在本站寻找。

    本文标签: