MyBatis高级映射、动态SQL、缓存、逆向工程

文章目录

MyBatis架构

  • 功能架构设计图:接触完了 大概学的差不多回头就能看的懂了

  • 功能架构

    • (1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
    • (2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
    • (3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
  • 框架原理

在这里插入图片描述

MyBatis的核心对象

  • SqlSessionFactoryBuilder, SqlSessionFactory, SqlSession和Mapper
  • 不用容器管理的话需要了解这些对象的创建销毁
SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。 换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。 这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

SqlSession session = sqlSessionFactory.openSession();
try {
  // 你的应用逻辑代码
} finally {
  session.close();
}

在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭。

映射器实例(Mapper

映射器是一些由你创建的、绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。 也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。 并不需要显式地关闭映射器实例,尽管在整个请求作用域保持映射器实例也不会有什么问题,但是你很快会发现,像 SqlSession 一样,在这个作用域上管理太多的资源的话会难于控制。 为了避免这种复杂性,最好把映射器放在方法作用域内。下面的示例就展示了这个实践:

SqlSession session = sqlSessionFactory.openSession();
try {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  // 你的应用逻辑代码
} finally {
  session.close();
}

这个就是我们在编程式的使用里面看到的四个对象的生命周期的总结。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrvtxSxg-1641296800551)(…/…/…/Image/%E6%88%AA%E5%9B%BE/typora%E5%9B%BE/1383365-20190627091402475-763763294.png)]

映射器分类
  • xml映射器 利用 SqlSession的各种方法实现 sqlSession.selectOne("...xml", 1);
  • xml 映射器 + 接口映射器 sqlSession.getMapper(接口.class) 去调用映射的方法
  • 注解 + 接口映射器:将 xml 里面的sql配置信息 变成 Java 注解的形式写到接口映射器
引入映射器的方式

mybatis 的全局配置文件 mappers标签

  • 文件路径引入 xml 映射器 <mapper resource="...xml"/>
  • 包名引入:<mapper name=".../.../...xml"/>
  • 类注册引入:<mapper class="cn.mybatis.mydemo.mapper.StudentMapper"/>

配置详解

MyBatisconfig 配置文件 大多数用的默认的 所以配置的不多。

<?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="db.properties"></properties>
    <settings>
        <!-- 打印查询语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
        <!-- 控制全局缓存(二级缓存)-->
        <setting name="cacheEnabled" value="true"/>
        <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
        <!--<setting name="proxyFactory" value="CGLIB" />-->
        <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
        <!--
                <setting name="localCacheScope" value="STATEMENT"/>
        -->
        <setting name="localCacheScope" value="SESSION"/>
    </settings>
	<!-- 起别名 -->
    <typeAliases>
        <!-- 扫包  两种方式存一
 		<package name="包名"/>
		-->
        <typeAlias alias="blog" type="com.wuzz.domain.Blog" />
    </typeAliases>

<!--    <typeHandlers>
        <typeHandler handler="com.wuzz.type.MyTypeHandler"></typeHandler>
    </typeHandlers>-->

    <!-- 对象工厂 -->
<!--    <objectFactory type="com.wuzz.objectfactory.GPObjectFactory">
        <property name="wuzz" value="666"/>
    </objectFactory>-->

<!--    <plugins>
        <plugin interceptor="com.wuzz.interceptor.SQLInterceptor">
            <property name="wuzz" value="betterme" />
        </plugin>
        <plugin interceptor="com.wuzz.interceptor.MyPageInterceptor">
        </plugin>
    </plugins>-->

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
            <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="BlogMapper.xml"/>
        <mapper resource="BlogMapperExt.xml"/>
    </mappers>
</configuration>
configuration

configuration 是整个配置文件的根标签,实际上也对应着MyBatis 里面最重要的配置类Configuration。它贯穿MyBatis 执行流程的每一个环节。这里面有很多的属性,跟其他的子标签也能对应上。

注意:MyBatis 全局配置文件顺序是固定的,否则启动的时候会报错。

properties

第一个是properties 标签,用来配置参数信息,比如最常见的数据库连接信息。为了避免直接把参数写死在xml 配置文件中,我们可以把这些参数单独放在properties 文件中,用properties 标签引入进来,然后在xml 配置文件中用${}引用就可以了。可以用resource 引用应用里面的相对路径,也可以用url 指定本地服务器或者网络的绝对路径。

我们为什么要把这些配置独立出来?有什么好处?或者说,公司的项目在打包的时候,有没有把properties 文件打包进去?

  1. 提取,利于多处引用,维护简单;
  2. 把配置文件放在外部,避免修改后重新编译打包,只需要重启应用;
  3. 程序和配置分离,提升数据的安全性,比如生产环境的密码只有运维人员掌握。
settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项的意图、默认值等。

设置名描述有效值默认值
cacheEnabled全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType属性来覆盖该项的开关状态。true | falsefalse
aggressiveLazyLoading当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本默认值为 true)
multipleResultSetsEnabled是否允许单一语句返回多结果集(需要驱动支持)。true | falsetrue
useColumnLabel使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。true | falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要驱动支持。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能支持但仍可正常工作(比如 Derby)。true | falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或者未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出提醒日志 ('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'的日志等级必须设置为 WARN)FAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
safeRowBoundsEnabled允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。SESSION | STATEMENTSESSION
jdbcTypeForNull当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType 常量,常用值:NULL, VARCHAR 或 OTHER。OTHER
lazyLoadTriggerMethods指定哪个对象的方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成的默认语言。一个类型别名或完全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)一个类型别名或完全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值初始化的时候比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。true | falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (如集合或关联)。(新增于 3.4.2)true | falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。任何字符串未设置
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
proxyFactory指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 以上)
vfsImpl指定 VFS 的实现自定义 VFS 的实现的类全限定名,以逗号分隔。未设置
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true | falsetrue
configurationFactory指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)类型别名或者全类名.未设置
typeAliases

TypeAlias 是类型的别名,跟Linux 系统里面的alias 一样,主要用来简化全路径类名的拼写。比如我们的参数类型和返回值类型都可能会用到我们的Bean,如果每个地方都配置全路径的话,那么内容就比较多,还可能会写错。我们可以为自己的Bean 创建别名,既可以指定单个类,也可以指定一个package,自动转换。配置了别名以后,只需要写别名就可以了。MyBatis 里面有系统预先定义好的类型别名,在TypeAliasRegistry 中。

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>

当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:

@Alias("author")
public class Author {
    ...
}
typeHandlers

由于Java 类型和数据库的JDBC 类型不是一一对应的(比如String 与varchar),所以我们把Java 对象转换为数据库的值,和把数据库的值转换成Java 对象,需要经过一定的转换,这两个方向的转换就要用到TypeHandler。MyBatis 已经内置了很多TypeHandler(在type 包下),它们全部注册在TypeHandlerRegistry 中,他们都继承了抽象类BaseTypeHandler,泛型就是要处理的Java 数据类型。

当我们做数据类型转换的时候,就会自动调用对应的TypeHandler 的方法。如果我们需要自定义一些类型转换规则,或者要在处理类型的时候做一些特殊的动作,就可以编写自己的TypeHandler,跟系统自定义的TypeHandler 一样,继承抽象类BaseTypeHandler。有4 个抽象方法必须实现,我们把它分成两类:set 方法从Java 类型转换成JDBC 类型的,get 方法是从JDBC 类型转换成Java 类型的。

img

比如我们想要在获取或者设置String 类型的时候做一些特殊处理,我们可以

写一个String 类型的TypeHandler
public class MyTypeHandler extends BaseTypeHandler<String> {
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
            throws SQLException {
        // 设置 String 类型的参数的时候调用,Java类型到JDBC类型
        // 注意只有在字段上添加typeHandler属性才会生效
        // insertBlog name字段
        System.out.println("---------------setNonNullParameter1:"+parameter);
        ps.setString(i, parameter);
    }

    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 根据列名获取 String 类型的参数的时候调用,JDBC类型到java类型
        // 注意只有在字段上添加typeHandler属性才会生效
        System.out.println("---------------getNullableResult1:"+columnName);
        return rs.getString(columnName);
    }

    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        // 根据下标获取 String 类型的参数的时候调用
        System.out.println("---------------getNullableResult2:"+columnIndex);
        return rs.getString(columnIndex);
    }

    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        System.out.println("---------------getNullableResult3:");
        return cs.getString(columnIndex);
    }
}
第二步,在mybatis-config.xml 文件中注册:
<typeHandlers>
<typeHandler handler="com.wuzz.type.MyTypeHandler"></typeHandler>
</typeHandlers>
第三步,在我们需要使用的字段上指定,

比如:插入值的时候,从Java 类型到JDBC 类型,在字段属性中指定typehandler:

<insert id="insertBlog" parameterType="com.wuzz.domain.Blog">
  insert into blog (bid, name, author_id)
  values (#{bid,jdbcType=INTEGER},
  #{name,jdbcType=VARCHAR,typeHandler=com.wuzz.type.MyTypeHandler},
  #{authorId,jdbcType=INTEGER})
</insert>

返回值的时候,从JDBC 类型到Java 类型,在resultMap 的列上指定typehandler:

<result column="name" property="name" jdbcType="VARCHAR"
typeHandler="com.wuzz.type.MyTypeHandler"/>
处理枚举类型

若想映射枚举类型 Enum,则需要从 EnumTypeHandler 或者 EnumOrdinalTypeHandler 中选一个来使用。

比如说我们想存储取近似值时用到的舍入模式。默认情况下,MyBatis 会利用 EnumTypeHandler 来把 Enum 值转换成对应的名字。

注意 EnumTypeHandler 在某种意义上来说是比较特别的,其他的处理器只针对某个特定的类,而它不同,它会处理任意继承了 Enum 的类。

不过,我们可能不想存储名字,相反我们的 DBA 会坚持使用整形值代码。那也一样轻而易举: 在配置文件中把 EnumOrdinalTypeHandler 加到 typeHandlers 中即可, 这样每个 RoundingMode 将通过他们的序数值来映射成对应的整形数值。

objectFactory

当我们把数据库返回的结果集转换为实体类的时候,需要创建对象的实例,由于我们不知道需要处理的类型是什么,有哪些属性,所以不能用new 的方式去创建。在MyBatis 里面,它提供了一个工厂类的接口,叫做ObjectFactory,专门用来创建对象的实例,里面定义了4 个方法。

public interface ObjectFactory {
    void setProperties(Properties var1);

    <T> T create(Class<T> var1);

    <T> T create(Class<T> var1, List<Class<?>> var2, List<Object> var3);

    <T> boolean isCollection(Class<T> var1);
}

ObjectFactory 有一个默认的实现类DefaultObjectFactory,创建对象的方法最终都调用了instantiateClass(),是通过反射来实现的。如果想要修改对象工厂在初始化实体类的时候的行为,就可以通过创建自己的对象工厂,继承DefaultObjectFactory 来实现(不需要再实现ObjectFactory 接口)。

public class MyObjectFactory extends DefaultObjectFactory {
    @Override
    public Object create(Class type) {
        System.out.println("创建对象方法:" + type);
        if (type.equals(Blog.class)) {
            Blog blog = (Blog) super.create(type);
            blog.setName("object factory");
            blog.setBid(1111);
            blog.setAuthorId(2222);
            return blog;
        }
        Object result = super.create(type);
        return result;
    }
}

我们可以直接用自定义的工厂类来创建对象:

public class ObjectFactoryTest {
    public static void main(String[] args) {
        MyObjectFactory factory = new MyObjectFactory();
        Blog myBlog = (Blog) factory.create(Blog.class);
        System.out.println(myBlog);
    }
}

应用场景举例:

比如有一个新锐手机品牌在一个电商平台上面卖货,为了让预约数量好看一点,只要有人预约,预约数量就自动乘以3。这个时候就可以创建一个ObjectFactory,只要是查询销量,就把它的预约数乘以3 返回这个实体类。

1、什么时候调用了objectFactory.create()?

创建DefaultResultSetHandler 的时候,和创建对象的时候。

2、创建对象后,已有的属性为什么被覆盖了?

DefaultResultSetHandler 类的395 行getRowValue()方法里面里面调用了applyPropertyMappings()

3、返回结果的时候,ObjectFactoryTypeHandler 哪个先工作?

先是ObjectFactory,再是TypeHandler。肯定是先创建对象。

step out 可以看到一步步调用的层级。

插件(plugins

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。

environments、environment

environments 标签用来管理数据库的环境,比如我们可以有开发环境、测试环境、生产环境的数据库。可以在不同的环境中使用不同的数据库地址或者类型。

<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
            <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>

一个environment 标签就是一个数据源,代表一个数据库。这里面有两个关键的标签,一个是事务管理器,一个是数据源。

transactionManager

如果配置的是JDBC,则会使用Connection 对象的commit()、rollback()、close()管理事务。如果配置成MANAGED,会把事务交给容器来管理,比如JBOSSWeblogic。因为我们跑的是本地程序,如果配置成MANAGE 不会有任何事务。如果是Spring + MyBatis , 则没有必要配置, 因为我们会直接在applicationContext.xml 里面配置数据源,覆盖MyBatis 的配置。

mappers

<mappers>标签配置的是我们的映射器,也就是Mapper.xml 的路径。这里配置的目的是让MyBatis 在启动的时候去扫描这些映射器,创建映射关系。我们有四种指定Mapper 文件的方式:

  1. 使用相对于类路径的资源引用(resource)
  2. 使用完全限定资源定位符(绝对路径)(URL)
  3. 使用映射器接口实现类的完全限定类名
  4. 将包内的映射器接口实现全部注册为映射器(最常用)
<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

映射文件(Mapper)

SQL映射文件的顶级元素
  • cache – 对给定命名空间的缓存配置。
  • cache-ref – 对其他命名空间缓存配置的引用。
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • sql – 可被其他语句引用的可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句
select
简单查询

#{...} 这就告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中。

${...}这个就是普通的字符串了,会拼接到 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.gpb.dao.GoodsMapper">
  <select id="selectGoods" parameterType="int" resultType="Goods">
    select * from goods where id = #{id}
  </select>
  <select id="findAll" resultType="com.gpb.bean.Goods">
  	select * from goods
  </select>
  <select id="findByName" parameterType="String" resultType="Goods">
  <!-- 直接拼接sql的话用'%${value}%'  固定写法 不能防止sql注入 不安全 -->
  	select * from goods where goodsName like "%"#{goodsName}"%"
  </select>
  <insert id="insertGoods" parameterType="Goods">
      <!-- 在插入数据之后查询id索引【设置自增的】最大值并返回 在实体里面能取出来 -->
  	<selectKey order="AFTER" keyProperty="id" resultType="Integer">
  		select last_insert_id()
  	</selectKey>
  		insert into goods(id, goodsName, price, memo, pic, createTime) values (#{id}, #{goodsName}, #{price}, #{memo}, #{pic}, #{createTime})
  </insert>
  <update id="updateGoods" parameterType="Goods">
  	update goods set goodsName=#{goodsName}, price=#{price},memo=#{memo},pic=#{pic},createTime=#{createTime} where id =#{id}
  </update>
  <delete id="delById" parameterType="int">
  	delete from goods where id=#{id}
  </delete>
</mapper>
相关属性描述:
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
resultType从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
resultMap外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
fetchSize这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)。
statementTypeSTATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false
resultSets这个设置仅对多结果集的情况适用。它将列出语句执行后返回的结果集并给每个结果集一个名称,名称是逗号分隔的。
insert, update 和 delete:
简单的插入,删除,修改:
相关属性描述:
属性描述
id命名空间中的唯一标识符,可被用来代表这条语句。
parameterType将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
statementTypeSTATEMENT,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 的语句;如果带或者不带的语句都有,则不带的会被忽略。
插入语句的配置规则更加丰富

在插入语句里面有一些额外的属性和子元素用来处理主键的生成,而且有多种生成方式。首先,如果你的数据库支持自动生成主键的字段(比如 MySQLSQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就 OK 了。如果你的数据库还支持多行插入, 你也可以传入一个数组或集合,并返回自动生成的主键。

<insert id="inserts" useGeneratedKeys="true"
            keyProperty="id">
     insert into user (name, author_id) values
     <foreach item="item" collection="list" separator=",">
         (#{item.name}, #{item.authorId})
     </foreach>
</insert>
SQL

这个元素可以被用来定义可重用的 SQL 代码段,这些 SQL 代码可以被包含在其他语句中。它可以(在加载的时候)被静态地设置参数。 在不同的包含语句中可以设置不同的值到参数占位符上。mybatissql标签与include标签进行配合,灵活的查询需要的数据。

<sql id="ref">
    bid,name,authorId
</sql>

<select id="selectbyId" resultMap="BaseResultMap">
    select
    <include refid="ref"/>
    from
    blog where bid = #{bid}
</select>
  • sql标签中id属性对应include标签中的refid属性。

  • 通过include标签将sql片段和原sql片段进行拼接成一个完整的sql语句进行执行。

  • include 标签中还可以用property标签用以指定自定义属性。

    <sql id="ref">
        ${abc},name,authorId
    </sql>
    
    <select id="selectbyId" resultMap="BaseResultMap">
        select
        <include refid="ref">
            <property name="abc" value="bid"/>
        </include>
        from
        blog where bid = #{bid}
    </select>
    

    sql标签中通过${}取出对应include标签中设置的属性值。

    动态sql
    <!-- sql标签 常用sql -->
      <sql id="queryUserInfoSearch">
        <if test="userName!=null and userName!=''">
          and userName = #{userName}
        </if>
        <if test="gender!=null and gender!=''">
          and gender = #{gender}
        </if>
        <if test="orderId!=null and orderId!=''">
          and id in(select userId from orders where id = #{orderId})
        </if>
      </sql>
    
      <select id="queryUserInfoBySearch" parameterType="hashmap" resultType="UserInfo">
        <!-- select * from userinfo where 1=1 这是原始写法 where会代替这个 它会自动去除第一个and 自动添加where -->
        select * from userinfo
        <where>
        <!-- include标签含入上面的sql片段 用id去对应 -->
          <include refid="queryUserInfoSearch"></include>
        </where>
      </select>
    </mapper>
    

    还有别的标签用法。

高级映射:

关联概述

在关系型数据库中,表与表之间很少是独立与其他表没关系的。所以在实际开发过程中我们会碰到很多复杂的关联关系。

常见的是一对一、一对多、多对多的关联关系。

关系数据库中:

  • 一对一:在任意一方引入对方主键作为外键;

  • 一对多:在“多”的一方,添加“一”的一方的主键作为外键;

  • 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。

java中实体类实现关联关系:

  • 一对一:把另一个实体当做这个实体的一个属性
  • 一对多:把另一个实体【多】的集合当做这个实体的一个属性
  • 多对多:通过中间实体实现多对多的关系或者把一对多双向化

MyBatis实现关联关系:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。【延迟加载实现】
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。【常用的一对一(association),一对多(collection),多对多【通过中间实体嵌套一层一层关联下去】】
延迟加载
  • 延迟加载:先从表单查询,需要时再去关联表去关联查询,大大提高数据库的性能。

  • 默认没有开启延迟加载,需要在MyBatis 的配置文件中去配置:

  • <settings>
            <!-- 开启延迟加载,注意必须写在前面 -->
                <!-- 打开延迟加载开关 -->
                <setting name="lazyLoadingEnabled" value="true"/>
                <!-- 将积极加载改为消极加载即是按需加载 -->
                <setting name="aggressiveLazyLoading" value="false"/>
    
    </settings>
    
N+1 查询问题是这样子的:
  • 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
  • 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。MyBatis 能够对这样的查询进行延迟加载,因此可以将大量语句同时运行的开销分散开来。mybatis.configuration.lazy-loading-enabled=true 可以开启延时加载 mybatis.configuration.aggressive-lazy-loading=true 可以指定哪些方法调用查询, 然而,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,就会触发所有的延迟加载查询,性能可能会变得很糟糕。所以还有另外一种方法。

关联嵌套的映射,分一对一,一对多和多对多三种

JDBC 要求,如果一个列允许null,并且会传递值为null的参数,那么必须要设置 jdbcType属性

  • 一对一

    • 两种实现方式【普通映射和 association
  • 一对多

    • collection
  • 多对多

    • 要在类里面按照层级映射关系一层一层声明

    • xml 配置的时候也要严格按照层级配置

      数据库表的对应关系:UserInfo —> Orders —> OrderDetail —> Goods【1:m 1:m 1:1 】

          <!-- 一个订单对应一个用户:一对一关系的映射 两种实现 用户和订单表-->
          <select id="findById" parameterType="integer" resultMap="findByIdMap2">
              select o.id id_, o.userid userid_ , o.createtime, o.memo,
                     u.userName, u.address, u.gender
              from Orders o join UserInfo u on o.userid = u.id where o.id = #{id}
          </select>
          <resultMap id="findByIdMap" type="com.gpb.bean.Orders">
              <id column="id_" property="id"/>
              <result column="userid_" property="userId"/>
              <result column="createtime" property="createTime"/>
              <result column="memo" property="memo"/>
              <!-- 同样的方式映射另一张表的字段 -->
              <result column="userName" property="userInfo.username"/>
          </resultMap>
          <resultMap id="findByIdMap2" type="Orders">
              <id column="id_" property="id"/>
              <result column="userid_" property="userId"/>
              <result column="createtime" property="createTime"/>
              <result column="memo" property="memo"/>
              <!-- 单独的映射另一张表 -->
              <association property="userInfo" javaType="UserInfo">
                  <result column="userName" property="username"/>
                  <result column="gender" property="gender"/>
                  <result column="address" property="address"/>
              </association>
          </resultMap>
          <!-- 一对多 订单表对订单细节表-->
          <select id="findOrderDetail" parameterType="integer" resultMap="findOrderDetailMap">
              select o.id oid, o.createtime, o.memo, o2.id, o2.goodsid, o2.itemsnum
                  from orders o join orderdetail o2 on o.id = o2.orderid where o.id = #{id}
          </select>
          <resultMap id="findOrderDetailMap" type="Orders">
              <id column="oid" property="id"/>
              <result column="createtime" property="createTime"/>
              <result column="memo" property="memo"/>
              <collection property="orderDetailList" ofType="OrderDetail">
                  <result column="id" property="id"/>
                  <result column="goodsid" property="goodsId"/>
                  <result column="itemsnum" property="itemsNum"/>
              </collection>
          </resultMap>
          <!-- 多对多 查询用户和购买商品的resultMap-->
          <select id="findOrdersMany" resultMap="findManyToMany">
              SELECT
                  userinfo.id uid,
                  userinfo.username,
                  userinfo.gender,
                  orders.id oid,
                  orders.memo,
                  orderdetail.id did,
                  orderdetail.itemsnum,
                  orderdetail.goodsid,
                  goods.id gid,
                  goods.goodsname
              FROM
                  orders,
                  userinfo,
                  orderdetail,
                  goods
              WHERE
                  orders.userid = userinfo.id AND orderdetail.orderid=orders.id
                AND orderdetail.goodsid=goods.id
          </select>
          <resultMap id="findManyToMany" type="UserInfo">
              <!-- 配置用户信息 -->
              <id column="uid" property="id"/>
              <result column="username" property="username"/>
              <result column="gender" property="gender"/>
              <!-- 配置订单信息 一个用户对应多个订单 collection -->
              <collection property="ordersList" ofType="Orders">
                  <result column="oid" property="id"/>
                  <result column="memo" property="memo"/>
      
              <!-- 配置订单明细信息 一个订单对应多个订单详情 collection -->
                  <collection property="orderDetailList" ofType="OrderDetail">
                      <id column="did" property="id"/>
                      <result column="itemsNum" property="itemsNum"/>
                      <result column="goodsid" property="goodsId"/>
                      <!-- 配置货物信息 一个订单明细对应一个商品 association -->
                          <association property="goods" javaType="Goods">
                              <id column="gid" property="id"/>
                              <result column="goodsname" property="goodsName"/>
                          </association>
                  </collection>
              </collection>
          </resultMap>
      </mapper>
      

resultMap可以实现高级映射(使用 association、collection 实现一对一和一对多的映射),这两个具备延迟加载功能。

延迟加载

概述

在真正使用数据的时候才发起查询,不用的时候不查询,按需加载,又叫懒加载。例如:一对多的关系,先查这个一的数据,然后在调用的时候才去查询哪个多的表的数据。一对一也是这样的。

四种关联关系:

  • 一对一,一对多:一般采用延迟加载

    • 将查询语句按单表查询【分两个select去查】

    • 把第一个表的数据全拿出来,然后把第二个select的id放到association里面 在 使用第二个一的数据的时候会自动去调用第二个表的select查询并返回数据

    • 实例:

      • 订单和商品详情【一对一】

        public class OrderDetail implements Serializable {
            private Integer id;
            private Integer orderId;
            private Integer goodsId;
            private Integer itemsNum;
            //订单详情和商品是一对一的关系
            private Goods goods;
            public OrderDetail() {
            }
            ...set、get
        }
        
        public class Goods implements Serializable {
        	private Integer id;
        	private String goodsName;
        	private Double price;
        	private String memo;
        	private String pic;
        	private Date createTime;
         ... 构造 set、get   
        }
        
        • 懒加载一对一【在接口中这两个select都要写】

        •     <select id="findGoods" parameterType="integer" resultType="Goods">
                  select goodsname from goods where id = #{goodsid}
              </select>
              <select id="findOrderDetail1" parameterType="integer" resultMap="orderDetail">
                  select orderid, goodsid from orderdetail where #{orderid}
              </select>
          
              <resultMap id="orderDetail" type="OrderDetail">
                  <result property="orderId" column="orderid"/>
                  <result property="goodsId" column="goodsid"/>
                  <association property="goods" column="goodsid" javaType="Goods" select="findGoods"/>
              </resultMap>
          
    • 一对多和一对一类似 但是用的是 collection 属性

    • 说明:必须在接口要对应方法;一对一的懒加载使用association 标签实现, 属性是 实体类的属性【对应另一个实体类的】,传过去的条件,一般是外键,然后查询的 id 对应上就好了。

  • 多对一,多对多:一般采用立即加载

缓存

概念

缓存可以将数据保存在内存中,是互联网系统常常用到的。目前流行的缓存服务器有 MongoDB、Redis、Ehcache 等。缓存是在计算机内存上保存的数据,读取时无需再从磁盘读入,因此具备快速读取和使用的特点。

和大多数持久化框架一样,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 只开启一级缓存。

一级缓存

一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。【commit

在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。

由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。

时序图:

在这里插入图片描述

二级缓存

原理:二级缓存区域是根据 mapper 的 namespace 划分的。每次查询会先从缓存区域查找,如果找不到就去数据库查询,并将查询到的数据存到缓存。MyBatis内部存储缓存使用的是HashMap, key 为 hashcode + sqlid + sql, value 是映射出来查询到的 java 对象。

二级缓存是全局缓存,作用域超出 session 范围之外,可以被所有 SqlSession 共享。【在同一个命名空间下】

一级缓存缓存的是 SQL 语句,二级缓存缓存的是结果对象。

sqlSession 执行更新语句等操作提交 commit 后会清空缓存区 防止脏读

二级缓存需要开启配置:

  • 在全局配置文件里面:<setting name="cacheEnabled" value="true">
  • 实体类实现序列化接口
  • 映射文件中配置:<cache/>

单独禁用:

  • statement中 使用属性:useCache = "false"

清空缓存:

  • statement中 使用属性:flushCache= "true"

其它设置:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />

属性说明
eviction代表的是缓存回收策略,目前 MyBatis 提供以下策略。LRU:使用较少,移除最长时间不用的对象;FIFO:先进先出,按对象进入缓存的顺序来移除它们;SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象;WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存。
size引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象。
readOnly只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存。

逆向工程

从 数据库 到 java ,利用工具生成数据库对应的实体表,映射文件,接口等。实现方法比较多。

etting name=“cacheEnabled” value=“true”>`

  • 实体类实现序列化接口
  • 映射文件中配置:<cache/>

单独禁用:

  • statement中 使用属性:useCache = "false"

清空缓存:

  • statement中 使用属性:flushCache= "true"

其它设置:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />

属性说明
eviction代表的是缓存回收策略,目前 MyBatis 提供以下策略。LRU:使用较少,移除最长时间不用的对象;FIFO:先进先出,按对象进入缓存的顺序来移除它们;SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象;WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存。
size引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象。
readOnly只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存。

逆向工程

从 数据库 到 java ,利用工具生成数据库对应的实体表,映射文件,接口等。实现方法比较多。

原文地址:https://blog.csdn.net/qq_43636709/article/details/122310139

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


1.pom.xml引入依赖 &lt;dependency&gt; &lt;groupId&gt;com.github.pagehelper&lt;/groupId&gt; &lt;artifactId&gt;pagehelper&lt;/artifactId&gt; &lt;version&gt;5
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt; &lt;!DOCTYPE configuration PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot; &qu
准备工作 ① 创建数据库&amp;数据表 ## 创建数据库 CREATE DATABASE `dbtest1`; ## 创建数据表 CREATE TABLE `t_user` ( `id` INT NOT NULL AUTO_INCREMENT, `username` VARCHAR(20) DEF
MyBatis逆向工程是指根据数据库表结构自动生成对应的实体类、Mapper接口以及SQL映射文件的过程。这个过程可以通过MyBatis提供的逆向工程工具来完成,极大地方便了开发人员,避免了重复的代码编写,提高了开发效率。 创建逆向工程的步骤 1、添加依赖&amp;插件 &lt;!-- 控制Mave
MyBatis获取参数值的两种方式:${}和#{} ${}的本质就是字符串拼接,#{}的本质就是占位符赋值。 ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自
resultMap作用是处理数据表中字段与java实体类中属性的映射关系。 准备工作 ① 创建数据库&amp;数据表 CREATE DATABASE `dbtest1`; CREATE TABLE `t_emp` ( `emp_id` int NOT NULL AUTO_INCREMENT, `em
EHCache缓存针对于MyBatis的二级缓存。 MyBatis默认二级缓存是SqlSessionFactory级别的。 添加依赖 &lt;!-- MyBatis-EHCache整合包 --&gt; &lt;dependency&gt; &lt;groupId&gt;org.mybatis.cac
MyBatis 提供了一级缓存和二级缓存的支持,用于提高数据库查询的性能,减少不必要的数据库访问。 一级缓存(SqlSession 级别的缓存) 一级缓存是 MyBatis 中最细粒度的缓存,也称为本地缓存。它存在于每个 SqlSession 的生命周期中,当 SqlSession 被关闭或清空时,
动态SQL是 MyBatis 中非常强大且灵活的功能,允许你根据不同的条件构建SQL查询。 这主要通过 &lt;if&gt;、&lt;choose&gt;、&lt;when&gt;、&lt;otherwise&gt;、&lt;foreach&gt;等标签实现。 查询场景 /** * 根据条件查询员工
本教程操作系统:windows10系统、DELL G3电脑。 MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。在 MyBatis 中,配置数据库连接是非常重要的第一步。下面将详细介绍如何配置 MyBatis 的
今天小编给大家分享的是MyBatis批量查询、插入、更新、删除如何实现,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。
今天小编给大家分享的是Mybatis操作多数据源实现的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获...
本篇文章和大家了解一下mybatis集成到spring的方式有哪些。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。1 前言1.1 集成spring前使用mybat...
今天小编给大家分享的是mybatis-plus分页查询的3种方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获...
本篇内容主要讲解“mybatis之BaseTypeHandler怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“mybatis...
这篇文章主要介绍了mybatisforeach怎么传两个参数的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇mybatisforeach怎...
这篇“MyBatis映射文件中parameterType与resultType怎么使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的...
这篇文章主要介绍“MyBatis怎么获取自动生成的键值”,在日常操作中,相信很多人在MyBatis怎么获取自动生成的键值问题上存在疑惑,小编查阅了各式资料,整理出
这篇文章主要讲解了“怎么去掉IntelliJIDEA中mybatis对应的xml文件警告”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入...
这篇文章主要介绍“MybatisPlus使用@TableId主键id自增长无效如何解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这...