2020-11-07 Mybatis

Mybatis配置

mysql 5.7 or 8.0

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

属性(Porperties)

我们也可以通过porperties属性来引用实现配置文件。可以在典型的java配置文件中配置,也可以在porperties中传递属性。

编写一个配置文件。

在核心配置文件中引入。

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

设置(settings)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tL4nEKDW-1604763337618)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101203731970.png)]

开启缓存和懒加载。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJArBak4-1604763337620)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101203631008.png)]

驼峰命名,与orcale有关。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X4FMACWe-1604763337621)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101203705948.png)]

打印日志

类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:

  • 方式1:
<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
  • 方式二
<!--配置类别名-->
<typeAliases>
    <typeAlias type="com.kyz.pojo.UserInfo" alias="userinfo"></typeAlias>
    <!--<package name="com.kyz.pojo"/>-->
</typeAliases>

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

<typeAliases>
        <typeAlias type="com.kyz.pojo.UserInfo"></typeAlias>
        <!--<package name="com.kyz.pojo"/>-->
    </typeAliases>
    即使不写alias,也会自动生成小写的Class名。

这里就会自动配置类型别名。

在UserMapper.xml中就可以使用省略的写法。

<select id="getUserListByInfo" resultType="userinfo">
    select * from Mybatis.user;
  </select>

    <select id="getUserById" parameterType="int" resultType="userinfo">
    select account from Mybatis.user where id =#{id};
  </select>
  • 方式3

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

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

类型处理器(typeHandlers)

对象工厂(objectFactory)

每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。比如:

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
  public Object create(Class type) {
    return super.create(type);
  }
  public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

ObjectFactory 接口很简单,它包含两个创建实例用的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的。 另外,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给 setProperties 方法。

插件(plugins)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XaOxM5lT-1604763337622)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101205122824.png)]

  • Mybatis-plus

  • 通用mapper

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 {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

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

提示 覆盖配置类

除了用插件来修改 MyBatis 核心行为以外,还可以通过完全覆盖配置类来达到目的。只需继承配置类后覆盖其中的某个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会极大影响 MyBatis 的行为,务请慎之又慎。

环境配置(environments)

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:

  • 每个数据库对应一个 SqlSessionFactory 实例

为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

如果忽略了环境参数,那么将会加载默认环境,如下所示:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);

environments 元素定义了如何配置环境。

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

注意一些关键点:

  • 默认使用的环境 ID(比如:default=“development”)。
  • 每个 environment 元素定义的环境 ID(比如:id=“development”)。
  • 事务管理器的配置(比如:type=“JDBC”)。
  • 数据源的配置(比如:type=“POOLED”)。

默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:

    <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
    </transactionManager>
    

提示 如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。

public interface TransactionFactory {
  default void setProperties(Properties props) { // 从 3.5.2 开始,该方法为默认方法
    // 空实现
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

在事务管理器实例化后,所有在 XML 中配置的属性将会被传递给 setProperties() 方法。你的实现还需要创建一个 Transaction 接口的实现类,这个接口也很简单:

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

使用这两个接口,你可以完全自定义 MyBatis 对事务的处理。

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):

UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

  • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息。

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

  • driver.encoding=UTF8

这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8encoding 属性给数据库驱动。

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:

  • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:

  • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
  • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:

  • env.encoding=UTF8

这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8encoding 属性。

你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory 来使用第三方数据源实现:

public interface DataSourceFactory {
  void setProperties(Properties props);
  DataSource getDataSource();
}

org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可被用作父类来构建新的数据源适配器,比如下面这段插入 C3P0 数据源所必需的代码:

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

  public C3P0DataSourceFactory() {
    this.dataSource = new ComboPooledDataSource();
  }
}

为了令其工作,记得在配置文件中为每个希望 MyBatis 调用的 setter 方法增加对应的属性。 下面是一个可以连接至 PostgreSQL 数据库的例子:

<dataSource type="org.myproject.C3P0DataSourceFactory">
  <property name="driver" value="org.postgresql.Driver"/>
  <property name="url" value="jdbc:postgresql:mydb"/>
  <property name="username" value="postgres"/>
  <property name="password" value="root"/>
</dataSource>

数据库厂商标识(databaseIdProvider)

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:

<databaseIdProvider type="DB_VENDOR" />

databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName() 返回的字符串。 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:

<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

在提供了属性别名时,databaseIdProvider 的 DB_VENDOR 实现会将 databaseId 设置为数据库产品名与属性中的名称第一个相匹配的值,如果没有匹配的属性,将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。

你可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider 并在 mybatis-config.xml 中注册来构建自己的 DatabaseIdProvider:

public interface DatabaseIdProvider {
  default void setProperties(Properties p) { // 从 3.5.2 开始,该方法为默认方法
    // 空实现
  }
  String getDatabaseId(DataSource dataSource) throws SQLException;
}

映射器(mappers)

!(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101205215692.png)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1AXHmNO2-1604763337623)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101205236386.png)]

使用class文件绑定会产生的一些问题。

  • 接口和配置文件必须同名
  • 接口和配置文件必须在同一个目录下 resources资源选择的是.XML文件

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:

<!-- 使用相对于类路径的资源引用 -->
<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>

这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要讨论的。

--------------配置👆----------------

junit单元测试

  <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

生命周期和SCOPE

理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjKmifUN-1604763337624)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101222209717.png)]

  • SqlSessionFactoryBuilder

    局部

    一旦创建了SqlSessionFactoryBuilder就不再需要它了

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

  • SqlSessionFactory

    数据库连接池

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次。

SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

  • SqlSession

连接到连接池的一个请求

放到一个方法内部,线程不安全,因此是不能够资源共享的。

用完之后需要关闭,否则资源会被占用。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Tao8WJe-1604763337625)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101212203621.png)]

错误的使用会导致严重的并发问题。

这里面的每一个mapper就代表一个具体的业务。

Result结果集Mapper映射

解决属性名和字段名不一致的问题。

新建一个项目,拷贝之前的,测试实体类字段不一致的情况。

private int id;
private String account;
private String pwd;

测试出现问题

*被转换成了id,account,pwd

解决方法:

  • 起别名 数据库中的字段 as pojo成员变量中的字段
 <select id="getUserById" parameterType="int" resultType="userinfo">
  select id,account,password as pwd from Mybatis.user where id =#{id};
</select>
  • ResultMap

    有冲突通过resultmap来解决,结果集映射

    id,account,password 数据库

    id,account,pwd 实体类

UserMapper.xml中的

<resultMap id="UserMap" type="userInfo">
      <result column="id" property="id"></result>
      <result column="account" property="account"></result>
      <result column="password" property="pwd"></result>
  </resultMap>

  <select id="getUserListByInfo" resultMap="UserMap">
  select * from Mybatis.user;
</select>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-STN54b4p-1604763337625)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101220241454.png)]

结果映射

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

之前你已经见过简单映射语句的示例,它们没有显式指定 resultMap。比如:

<select id="selectUsers" resultType="map">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section,
       P.id as post_id,
       P.blog_id as post_blog_id,
       P.author_id as post_author_id,
       P.created_on as post_created_on,
       P.section as post_section,
       P.subject as post_subject,
       P.draft as draft,
       P.body as post_body,
       C.id as comment_id,
       C.post_id as comment_post_id,
       C.name as comment_name,
       C.comment as comment_text,
       T.id as tag_id,
       T.name as tag_name
  from Blog B
       left outer join Author A on B.author_id = A.id
       left outer join Post P on B.id = P.blog_id
       left outer join Comment C on P.id = C.post_id
       left outer join Post_Tag PT on PT.post_id = P.id
       left outer join Tag T on PT.tag_id = T.id
  where B.id = #{id}
</select>
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>

了解核心思想。

  • resultMap 元素是 MyBatis 中最重要最强大的元素。

  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

  • 什么不一样设置什么字段。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9yM5hElI-1604763337626)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101221058358.png)]

  • 一对多和多对一

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6oSwgxI0-1604763337626)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101221221203.png)]

多对一和一对多

日志工厂logImpl

如果数据库查询语句出了错,那么日志就是最好的处理。

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HsfDVVaa-1604763337627)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101231524356.png)]

在Mybats具体使用哪个实现,由我们的设置实现。

在mybatis-config.xml中配置我们的日志设置。

mybatis本质当然还是jdbc了

LOG4j的实现

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

有一些严重的信息在严重时使用、轻微的信息在轻微时使用,灵活的配置不需要修改应用的代码。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EvKuUZNL-1604763337627)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201101233626722.png)]

DEBUG的我们再我们再输出

1.先导入log4j的包

log4j log4j 1.2.17

2.配置logj4.properties

#############
# 输出到控制台
#############

# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
# WARN:日志级别     CONSOLE:输出位置自己定义的一个名字       logfile:输出位置自己定义的一个名字
log4j.rootLogger=WARN,CONSOLE,logfile
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 
# 配置CONSOLE日志的输出格式  [frame] 2019-08-22 22:52:12,000  %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n

################
# 输出到日志文件中
################

# 配置logfile输出到文件中 文件大小到达指定尺寸的时候产生新的日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# 保存编码格式
log4j.appender.logfile.Encoding=UTF-8
# 输出文件位置此为项目根目录下的logs文件夹中
log4j.appender.logfile.File=logs/root.log
# 后缀可以是KB,MB,GB达到该大小后创建新的日志文件
log4j.appender.logfile.MaxFileSize=10MB
# 设置滚定文件的最大值3 指可以产生root.log.1、root.log.2、root.log.3和root.log四个日志文件
log4j.appender.logfile.MaxBackupIndex=3  
# 配置logfile为自定义布局模式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

##########################
# 对不同的类输出不同的日志文件
##########################

# club.bagedate包下的日志单独输出
log4j.logger.club.bagedate=DEBUG,bagedate
# 设置为false该日志信息就不会加入到rootLogger中了
log4j.additivity.club.bagedate=false
# 下面就和上面配置一样了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

3.settings的配置

<properties resource="db.properties"></properties>

加载初始化

import org.apache.log4j.Logger;
static Logger logger = Logger.getLogger(userDaoTest.class);


logger.info("info:进入了log4jtest info");
logger.debug("debug:进入了log4jtest debug");
logger.error("error:进入了log4jtest error");

分页limit和RowBounds

  • limit

1.编写接口

//手动limit分页
List<UserInfo> getUserListByInfoLimit(Map<String,Integer> map);

使用万能的HashMap来输入信息。

2.在.xml中注册接口

<select id="getUserListByInfoLimit" parameterType="map" resultMap="UserMap">
    select * from  Mybatis.user limit #{startIndex},#{pageSize};
</select>

注意这里的键值对值要和自己写的Test类中的HashMap的值相对应。

3.写@Test

public void SelectAllLimit(){
    SqlSession sqlsession = MybatisUtil.getSqlsession();
    UserMapper mapper = sqlsession.getMapper(UserMapper.class);
    HashMap<String,Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",2);
    map.put("pageSize",2);
    List<UserInfo>userInfos = mapper.getUserListByInfoLimit(map);
    for (UserInfo user :userInfos
            ) {
        System.out.println(user);
    }
    sqlsession.close();
    }

本质上给sql传递了参数,这种方式我们仍然手动写了sql。

  • RowBounds

只需要写最简单的sql

这种方式用到了Mybatis不推荐的sql执行方法。没有通过mapper调用接口。

1.编写接口

//RowBounds分页  不需要设定参数
List<UserInfo> getUserListByInfoRowBounds();

2.在BlogMapper.xml中注册

 <select id="getUserListByInfoRowBounds" resultMap="UserMap">
        select * from Mybatis.user;
    </select>

3.编写@Test

@Test//RowBounds分页
    public void SelectAllRowBounds(){
    SqlSession sqlsession = MybatisUtil.getSqlsession();
    //RowBounds实现
    RowBounds rowBounds = new RowBounds(2, 2);
    //通过java代码层面实现分页
    List<UserInfo> userInfos =        sqlsession.selectList("com.kyz.dao.UserMapper.getUserListByInfoRowBounds",null,rowBounds);
    for (UserInfo user:userInfos
         ) {
        System.out.println(user);
    }
    sqlsession.close();
}

需要创建RowBounds对象,默认的RowBounds无参构造方法提供了一个最大值的查询方法。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.session;

public class RowBounds {
    public static final int NO_ROW_OFFSET = 0;
    public static final int NO_ROW_LIMIT = 2147483647;
    public static final RowBounds DEFAULT = new RowBounds();
    private final int offset;
    private final int limit;

    public RowBounds() {
        this.offset = 0;
        this.limit = 2147483647;
    }

    public RowBounds(int offset, int limit) {
        this.offset = offset;
        this.limit = limit;
    }

    public int getOffset() {
        return this.offset;
    }

    public int getLimit() {
        return this.limit;
    }
}
  • 分页插件

    学完Spring再配置这个插件。

MyBatis 分页插件 PageHelper

https://pagehelper.github.io/

  • MyBatis 分页插件 PageHelper

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CaOJ6cOR-1604763337628)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201102095657284.png)]

使用注解开发

面向接口编程

异步

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDIesB7c-1604763337629)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201102100030696.png)]

使用这种方式结果集映射不能实现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lmY2bdjS-1604763337629)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201102101112836.png)]

通过反射拿到了包名。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JpbibbIU-1604763337630)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201102101655348.png)]

代理模式,本质是反射

代理、工厂、单例模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5amA3NkA-1604763337630)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201102101929020.png)]

Mybatis详细的执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c2oYP5iq-1604763337631)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201102121209846.png)]

注解CRUD

基础变量类型的必须要加上@Pare(“uid”),只有一个变量的时候可以不加,但是最好加上,多个变量的时候必须加上。

LomBok 和 Grep Console

Plain Ordinary Java Object pojo内省去很多操作步骤。

  • java library
  • plugs
  • build tools

自动优化一些变量的设置。

1.安装lombok插件

2.导入jar包

3.再也不用写一些getset方法了,只需要加一些注解就可以了。

省掉 alt+insert import lombok.Data; @Data 再也不需要写get set tostring constructor等

在实体类上加注解即可。

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode

但是lombok是有问题的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JYkwGS4B-1604763337632)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201103203816343.png)]

一个源码有很多这样的插件,程序可读性会大大降低。

无需追求高大上的技术、适合的才是最好的。

多对一 和 一对多

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QFwIFpfo-1604763337632)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201103204106981.png)]

  • 多个学生对应一个老师

  • 多个学生关联一个老师

  • 对于老师而言这是一个一对多的关系。(集合)

    association – 一个复杂类型的关联;许多结果将包装成这种类型
    嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
    collection – 一个复杂类型的集合
    嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
    discriminator – 使用结果值来决定使用哪个 resultMap
    case – 基于某些值的结果映射
    嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
    

    环境搭建 SQL的执行速度非常快,很容易在表还没有创建好的时候就进行插入操作造成错误。


CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
  • 多个学生对应一个老师(学生角度)

对象使用association 多对一查询的方式

SQL

  • 子查询
  • 联表查询

集合使用collection

通过结果来驱动。

按照子查询处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vdijcn3p-1604763337633)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201103225254056.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYdnGYGf-1604763337633)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201103225316134.png)]

子查询方式

package com.kyz.dao;

import com.kyz.pojo.Student;

import java.util.List;

public interface StudentMapper {
    //查询所有的学生的信息,以及对应的老师的信息 子查询方式
     List<Student> studentTeacher();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--核心配置文件-->
<mapper namespace="com.kyz.dao.StudentMapper">
    <!--子查询嵌套的方式-->
    <select id="studentTeacher" resultMap="studentandteacher">
        select * from school.student
    </select>
    <resultMap id="studentandteacher" type="student">
        <result property="id" column="id"></result>
        <result property="name" column="name"></result>
        <association property="teacher" column="tid" javaType="teacher" select="Teacher">
        </association>
    </resultMap>
    <select id="Teacher" resultType="teacher">
        select * from school.teacher where  id =#{id};
    </select>
</mapper>

按照结果嵌套处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F8hZQloK-1604763337633)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201103230205628.png)]

 //查询所有学会的信息,以及对应的老师的信息 嵌套查询方式
 List<Student> studentTeacherNesting();
     
<!--嵌套查询的方式-->
    <select id="studentTeacherNesting" resultMap="studentandteacherN">
        select s.id sid,s.name sname,t.id tid,t.name tname
        from  school.student s,school.teacher t
        where s.tid = t.id;
    </select>
    <resultMap id="studentandteacherN" type="student">
        <result property="id" column="sid"></result>
        <result property="name" column="sname"></result>
        <association property="teacher" javaType="teacher">
            <result property="id"   column="tid"></result>
            <result property="name" column="tname"></result>
        </association>
    </resultMap>
  • 一个老师拥有多个学生(老师角度)

    按照结果嵌套处理

    <!--按照结果嵌套的方式-->
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from school.teacher t,school.student s
        where s.tid = t.id and t.id = #{tid}
    </select>
    <resultMap id="TeacherStudent" type="teacher">
        <result property="id" column="tid"></result>
        <result property="name" column="tname"></result>
        <collection property="student" ofType="student">
            <result property="id" column="sid"></result>
            <result property="name" column="sname"></result>
            <result property="tid" column="tid"></result>
        </collection>
    </resultMap>
    

子查询嵌套处理

property与column一样就不用再写

<!--按照子查询嵌套的方式-->
    <select id="getTeacher2" resultMap="TeacherStudent2">
        select * from school.teacher where id = #{tid};
    </select>
    <resultMap id="TeacherStudent2"  type="teacher">
        <collection property="students" javaType="ArrayList" ofType="student" select="TeacherStudent2in" column="id"></collection>
    </resultMap>
    <select id="TeacherStudent2in" resultType="student">
        select * from school.student where tid = #{tid};
    </select>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56E31Mgk-1604763337634)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201105203333235.png)]


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JFtqngOz-1604763337634)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201105203725640.png)]

调整sql与调整resultMap

按结果处理、按子查询嵌套处理、推荐使用按结果查询处理。

  • associacion

多对一 许多个学生选了一门老师的课程,按照学生查询处理

  • collection

一个包含很多个 private Liststudents

  • javaType & ofType

1.javaType用来执行实体类中属性的方法。

2.ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型。

注意点:

  • 保证sql的可读性,尽量保证通俗易懂
  • 注意多对一和一对多中,属性名和字段的问题
  • 如果问题不容易排查,可以使用日志,建议使用log4j
  • result中不能写错不能多打空格

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXqQjvsO-1604763337635)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201105205214835.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zmXp2PPC-1604763337635)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201105205323406.png)]

慢SQL 数据库的重要性

慢SQL的问题 好的查询方式1s 错误的方式1000s

加索引

面试问题

  • Mysql引擎
  • InnoDB的底层原理
  • 索引
  • 索引优化

动态SQL

动态sql就是在拼接sql语句,我们只要保证sql的正确性,按sql的格式去排列组合就行。

表的联查是可能通过链接来跨服务器查询的,我们可能要去考虑性能问题。

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

先在数据库新建查询中去把sql写出来,再去动态sql种拼接sql。

搭建环境很痛苦

  • 动态SQL就是根据不同的条件生成不同的SQL语句

也不会特别开心2333

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

搭建环境

CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
  • .xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kyz.dao.BlogMapper">


    
</mapper>

  • String对应char
@SuppressWarnings("all")//抑制警告
  • 驼峰命名规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y2jIPW14-1604763337636)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201105212603097.png)]

java代码生成数据库

package com.kyz.test;
import com.kyz.dao.BlogMapper;
import com.kyz.pojo.Blog;
import com.kyz.utils.IDutil;
import com.kyz.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;
import java.util.Date;
public class blogtest {
    static Logger logger = Logger.getLogger(BlogMapper.class); //import org.apache.log4j.Logger;
    @Test
    public void AddBolg(){
        SqlSession sqlsession = MybatisUtil.getSqlsession();
        BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
//1
        Blog blog = new Blog();
        blog.setId(IDutil.getID());
        blog.setTitle("Spring");
        blog.setAuthor("apache");
        blog.setCreateTime(new Date());
        blog.setViews(1234);
        mapper.AddBlog(blog);
//2
        blog.setId(IDutil.getID());
        blog.setTitle("Spring MVC");
        mapper.AddBlog(blog);
//3
        blog.setId(IDutil.getID());
        blog.setTitle("Spring MVC");
        mapper.AddBlog(blog);
//4
        blog.setId(IDutil.getID());
        blog.setTitle("Spring boot");
        mapper.AddBlog(blog);
//5
        blog.setId(IDutil.getID());
        blog.setTitle("Mybatis");
        mapper.AddBlog(blog);
//6
        blog.setId(IDutil.getID());
        blog.setTitle("Mybatis-plus");
        mapper.AddBlog(blog);
       sqlsession.commit();
        sqlsession.close();
    }
}

动态SQL IF

我不给串参数也要能查出来数据

1,泛型的定义以及存在意义
泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
例如:GenericClass<T>{}

一些常用的泛型类型变量:
E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)

hashmap泛型

万能Map

HashMap<String,Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",2);
    map.put("pageSize",2);
 List<UserInfo> getUserListByInfoLimit(Map<String,Integer> map);

resultMap映射的问题

property对应的是pojo   column对应的是数据库

for each中应该sout的应该是P p:ps 中的p

  1. if实现的动态查询

    <select id="queryBlogIf" parameterType="map" resultMap="blogmap">
        select * from school.blog where 1=1
        <if test="title!=null">
            and title = #{title}
        </if>
        <if test="author!=null">
            and author = #{author}
        </if>
    </select>
    </mapper>
    
  • 另一种实现方式是通过重载的方式

     List<Blog> queryBlogIf(Map map);
     List<Blog> queryBlogIf(int,int);
     List<Blog> queryBlogIf(int);
     List<Blog> queryBlogIf(sting);
     List<Blog> queryBlogIf(3);
     List<Blog> queryBlogIf(2);
     List<Blog> queryBlogIf();
    
    这种方式容易造成代码冗余,感觉不好用
    

动态SQL choose、trim

  • choose(when,overwise)

choose有点像switch语句

只会选择其中的一个来实现

<select id="queryBlogIfchoose" parameterType="map" resultMap="blogmap">
        select * from school.blog
        <where>
            <choose>
                <when test="title!=null">
                    title = #{title}
                </when>
                <when test="author!=null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views > #{views}
                </otherwise>
            </choose>
        </where>
    </select>

可以自动匹配结果。能够自动判断where该不该加入到sql语句当中去。

  • trim(where,set) trim可以自定义

where标签的使用
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<select id="queryBlogIfChoose" parameterType="map" resultMap="blogmap">
        select * from school.blog
        <where>
            <if test="title!=null">
                and title = #{title}
            </if>
            <if test="author!=null">
                and author = #{author}
            </if>
        </where>
    </select>

可以自动匹配结果。能够自动判断where该不该加入到sql语句当中去。

Update语句

 <update id="queryBolgupdate">
        update school.blog
        <set>
            <if test="title!=null">
                title = #{title},
            </if>
            <if test="author!=author">
                author = #{author}
            </if>
        </set>
        where  id = #{id}
    </update>

三项都包括的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hWeS7htP-1604763337636)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201106123658472.png)]

两项都包括的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mC8rzimq-1604763337637)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201106123756666.png)]

语句不完整报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCUn2HZ7-1604763337637)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201106123847486.png)]

用了set会自动去除掉语句中最后的“ , ”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wb4UBGyB-1604763337638)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201106124137749.png)]

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

本质还是sql语句,只不过可以在sql层面做一些动态代码,在执行sql的时候做了一些逻辑代码。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

SQL片段

1.把重复的、公用的sql片段用sql标签给抽取出来

注意事项:最好基于单表来做复用

有时候我们会把一些公共的部分抽取出来,方便复用

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>
    
   ---->
    <update id="updateAuthorIfNecessary">
  update Author
    <set>
        <include refid="SQLfragment"></include>
    </set>
  where id=#{id}
</update>
    +
    <sql id="SQLfragment">
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </sql>

动态SQL foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

举例说明

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>
select * from school.user where 1=1 and (id=1 or id=2 or id=3)
这种时候
  <foreach item="id" index="index" collection="ids"
      open="(" separator="or" close=")">
        #{item}
  </foreach>

事件案例

我们来查询id

 @Test
    public void queryBlogForeach(){
        SqlSession sqlsession = MybatisUtil.getSqlsession();
        BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        ArrayList<String> ids = new ArrayList<String>();
        map.put("ids",ids);
        //添加ids中的信息
        ids.add("53ddbf48f96c46669a600c0b93c366a0");
        ids.add("9d8abbe4ea36407ea6c34d9e288e1c18");
        List<Blog> blogs = mapper.queryBlogForeach(map);
        for (Blog blog:blogs
             ) {
            System.out.println(blog);
        }
        sqlsession.close();
    }

-----------------------------------------------------------------------------
    <!--xml-->
    <select id="queryBlogForeach" resultMap="blogmap" parameterType="map">
        select * from school.blog
        <where>
            <foreach collection="ids" item="id" open="(" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>

collection表示一个集合,这里我们用ArrayList ids,来存放我们想要查询结果的id。

open表示foreach遍历到数组元素后sql语句拼接时的首位,separator表示sql语句拼接的末尾。

简单了解下web渗透中的sql注入问题

1、熟悉asp,php、jsp等主流的Web安全技术,包括SQL注入、XSS、CSRF、命令执行、越权等常见的安全漏洞;
2、熟练掌握OWASP TOP10漏洞的利用手法,了解Apache、Nginx、IIS等中间件漏洞;
3、熟悉常用安全工具,如:AppScan、wvs、Burp suite、Nmap、Sqlmap等。
优先考虑的条件:
1、精通多种安全技术,掌握或熟悉各种攻击与防护技术;
2、具有一定的编程能力,可以自己编写简单的攻击和检测程序;

UUID

Universally Unique Identifier不会重复

缓存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KM7LA9Ck-1604763337638)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201107120203396.png)]

1.什么是缓存【cache】?

  • 存在内存中的数据。
  • 将用户经常查询的数据放在缓存中,用户去查询就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

3.什么样的数据使用缓存?

  • 经常查询且不经常改变的数据,反之不经常查询且经常改变的数据不能放在缓存中。

高并发 高可用 高性能

读写分离、主从复制:如何保证数据库中的数据是一致的。

用来解决这些问题。

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

查询 链接数据库 会很耗资源
一次查询的结果,暂存到一个可以直接取到的地方。

Mybatis缓存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9vAu4KT-1604763337639)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201107120203396.png)]

1.什么是缓存【cache】?

  • 存在内存中的数据。
  • 将用户经常查询的数据放在缓存中,用户去查询就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

3.什么样的数据使用缓存?

  • 经常查询且不经常改变的数据,反之不经常查询且经常改变的数据不能放在缓存中。

高并发 高可用 高性能

读写分离、主从复制:如何保证数据库中的数据是一致的。

用来解决这些问题。

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

查询 链接数据库 会很耗资源
一次查询的结果,暂存到一个可以直接取到的地方。
  • Mybatis包含一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存。缓存可以极大的提升查询效果

  • Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存

    1. 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    2. 二级缓存需要手动开启和配置,是基于namespce级别的缓存。代表一个Mapper级别的缓存。
    3. 为了提高扩展性,Mybatis定义了缓存接口cache。我们可以通过实现cache接口来自定义二级缓存。
    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package org.apache.ibatis.cache;
    
    import java.util.concurrent.locks.ReadWriteLock;
    
    public interface Cache {
        String getId();
    
        void putObject(Object var1, Object var2);
    
        Object getObject(Object var1);
    
        Object removeObject(Object var1);
    
        void clear();
    
        int getSize();
    
        default ReadWriteLock getReadWriteLock() {
            return null;
        }
    }
    

一级缓存

sqlSessionFactory.getSqlSession();

DEBUG - Cache Hit Ratio [com.kyz.dao.TeacherMapper]: 0.0
//缓存命中率

import java.io.Serializable;

public class Teacher implements Serializable 
  • 一级缓存也叫做本地缓存

    1.开启日志

    2.在test中查询两次相同的记录 在session中相同的记录

    3.在log中查看

在两次查询一号用户之间加入update 2号用户的结果。

三条sql,没有使用缓存

1.增删改可能会修改原来的数据,所以会失效

2.查询不同的数据

3.查询不同的Mapper.xml

4.sqlsession.clearCache();

        SqlSession sqlsession = MybatisUtil.getSqlsession();
        TeacherMapper mapper = sqlsession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getQueryById(4);
        System.out.println(teacher.toString());
        System.out.println("===两次相同的查询===");
        sqlsession.clearCache();
        Teacher teacher1 = mapper.getQueryById(4);

        sqlsession.close();

一级缓存是默认开启的,只在一次sqlsession中有效,这就是从拿到连接到关闭这个连接为止。

一级缓存相当于一个map

二级缓存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HD0TqUbl-1604763337639)(file://C:/Users/kyz/AppData/Roaming/Typora/typora-user-images/image-20201107224453717.png?lastModify=1604762415)]

首先需要开启二级缓存

如果当前会话关闭了,会将信息存到二级缓存中,不同的mapper查出来的数据会放到自己的map中

1.核心配置文件 设置

<setting name="cacheEnabled" value="true"/>

2.在当前Mapper.xml中设置

3.也可以自己增加一些参数

/*不同的mapper*/
    @Test
    public void mappergetqueryByID(){
        //sqlsession 1
        SqlSession sqlsession = MybatisUtil.getSqlsession();TeacherMapper mapper = sqlsession.getMapper(TeacherMapper.class);
        //sqlsession 2
        SqlSession sqlsession1 = MybatisUtil.getSqlsession();TeacherMapper mapper1 = sqlsession1.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getQueryById(4);System.out.println(teacher.toString());
        sqlsession.close();
        System.out.println("===两次不同mapper的查询===");
        Teacher teacher1 = mapper1.getQueryById(4);System.out.println(teacher1.toString());
        sqlsession1.close();
    }


当sqlsession也就是sqlsession.close关闭时,会开启二级缓存,前提是在一个sqlsession
    
    
DEBUG - Cache Hit Ratio [com.kyz.dao.TeacherMapper]: 0.0
DEBUG - Opening JDBC Connection
DEBUG - Created connection 7967307.
DEBUG - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@79924b]
DEBUG - ==>  Preparing: select * from school.teacher where id = ?;
DEBUG - ==> Parameters: 4(Integer)
DEBUG - <==      Total: 1
Teacher{id=4, name='孙老师'}
DEBUG - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@79924b]
DEBUG - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@79924b]
DEBUG - Returned connection 7967307 to pool.
===两次不同mapper的查询===
DEBUG - Cache Hit Ratio [com.kyz.dao.TeacherMapper]: 0.5
Teacher{id=4, name='孙老师'}

Cache Hit Ratio (缓冲命中率)

我们需要将缓存pojo序列化

implement Serializable
    
序列化          索引
serialize      Indexes
  • 只要开启了二级缓存,在同一个mapper中就有效
  • 数据会先存放在一级缓存当中
  • 只有当会话提交,或者会话关闭的时候才会提交到二级缓存中去。

缓存原理

  • 第一次查询走数据库,然后放在一级缓存中。
  • sqlsession关闭时,一级缓存的内容会存放到二级缓存中去。
  • 先去二级缓存中查询,再去一级缓存中查询。

useCache=“false” 查询的缓存是可以关闭的。

自定义缓存Ehcache

广泛使用的分布式缓存。

Redis做缓存。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dUtm3mnV-1604763337640)(C:\Users\kyz\AppData\Roaming\Typora\typora-user-images\image-20201107224839970.png)]

1.导入maven依赖

<dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>

2.在xxxMapper.xml中配置信息

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

3.书写配置文件ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>
public class EhcacheCache extends AbstractEhcacheCache {
    public EhcacheCache(String id) {
        super(id);
        if (!CACHE_MANAGER.cacheExists(id)) {
            CACHE_MANAGER.addCache(id);
        }

        this.cache = CACHE_MANAGER.getEhcache(id);
    }
}

Redis

设置(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' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定数据库驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
defaultResultSetType指定语句默认的滚动策略。(新增于 3.5.2)FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)未设置 (null)
safeRowBoundsEnabled是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 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)一个类型别名或完全限定类名。未设置
shrinkWhitespacesInSql从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)true | falsefalse
defaultSqlProviderTypeSpecifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.A type alias or fully qualifieNot set
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值