/**PageBeginHtml Block Begin **/ /***自定义返回顶部小火箭***/ /*生成博客目录的JS 开始*/ /*生成博客目录的JS 结束*/

mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法

imageimage

sqlmapconfig.xml配置


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

configuration 配置

properties 属性

settings 设置

typeAliases 类型别名

typeHandlers 类型处理器

objectFactory 对象工厂

plugins 插件

environments 环境

environment 环境变量

transactionManager 事务管理器

dataSource 数据源

databaseIdProvider 数据库厂商标识

mappers 映射器



properties

这些属性都是可外部配置且可动态替换的,既可以在典型的 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

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

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


<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>



typeAliases

类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:

<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>


当这样配置时,Blog可以用在任何使用domain.blog.Blog的地方。

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

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

这是一些为常见的 Java 类型内建的相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator



typeHandlers

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

型处理器 Java 类型 JDBC 类型
BooleanTypeHandler java.lang.Boolean, boolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Byte, byte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Short, short 数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandler java.lang.Integer, int 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Long, long 数据库兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandler java.lang.Float, float 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Double, double 数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHAR, VARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOB, LONGVARCHAR
NStringTypeHandler java.lang.String NVARCHAR, NCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOB, LONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER 或未指定类型
EnumTypeHandler Enumeration Type VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)。
InstantTypeHandler java.time.Instant TIMESTAMP
LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
LocalDateTypeHandler java.time.LocalDate DATE
LocalTimeTypeHandler java.time.LocalTime TIME
OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
OffsetTimeTypeHandler java.time.OffsetTime TIME
ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
YearTypeHandler java.time.Year INTEGER
MonthTypeHandler java.time.Month INTEGER
YearMonthTypeHandler java.time.YearMonth VARCHAR or LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE


你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。比如:


// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return cs.getString(columnIndex);
  }
}


<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>


处理枚举类型


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

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

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

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

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</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)


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)


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

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

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

<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 {
  void setProperties(Properties props);
  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;
}


数据源(dataSource)

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

许多 MyBatis 的应用程序会按示例中的例子来配置数据源。虽然这是可选的,但为了使用延迟加载,数据源是必须配置的。
有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):

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

  • driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。

作为可选项,你也可以传递属性给数据库驱动。要这样做,属性的前缀为“driver.”,例如:driver.encoding=UTF8


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

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

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

  • poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程. 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值: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))。这是个可选属性,如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。
  • data_source – 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。


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

  •                    env.encoding=UT

这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8 的 encoding 属性。

你可以通过实现接口 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();
  }
}


<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" />



这里的 DB_VENDOR 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置。 由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短,如下:

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


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

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

public interface DatabaseIdProvider {
  void setProperties(Properties p);
  String getDatabaseId(DataSource dataSource) throws SQLException;
}


映射器(mappers)


既然 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>




Mapper XML 文件(sql、insert、update、delete)

MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。

SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

  • cache – 给定命名空间的缓存配置。
  • cache-ref – 其他命名空间缓存配置的引用。
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • parameterMap – 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
  • sql – 可被其他语句引用的可重用语句块。
      • insert – 映射插入语句
      • update – 映射更新语句
      • delete – 映射删除语句
      • select – 映射查询语句

select


查询语句是 MyBatis 中最常用的元素之一,光能把数据存到数据库中价值并不大,如果还能重新取出来才有用,多数应用也都是查询比修改要频繁。对每个插入、更新或删除操作,通常对应多个查询操作。这是 MyBatis 的基本原则之一,也是将焦点和努力放到查询和结果映射的原因。简单查询的 select 元素是非常简单的。比如:


<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>


这个语句被称作 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。

注意参数符号:#{id},这就告诉 MyBatis 创建一个预处理语句参数,通过 JDBC,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:

// Similar JDBC code, NOT MyBatis…
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);

select 元素有很多属性允许你配置,来决定每条语句的作用细节。

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10000"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">


属性 描述
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(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。


insert, update 和 delete

数据变更语句 insert,update 和 delete 的实现非常接近:

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">


属性 描述
id 命名空间中的唯一标识符,可被用来代表这条语句。
parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
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 的语句;如果带或者不带的语句都有,则不带的会被忽略。



<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
</insert>

<update id="updateAuthor">
  update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}
</update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
</delete>


如前所述,插入语句的配置规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理主键的生成,而且有多种生成方式。

首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了。例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句可以修改为:

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>


image

image




image

image

image


image






sql

这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如:


<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
<!-- 这个 SQL 片段可以被包含在其他语句中 -->
<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>


<!--属性值也可以被用在 include 元素的 refid 属性里-->
<include refid="${include_target}"/>

<sql id="sometable">
  ${prefix}Table
</sql>

<sql id="someinclude">
  from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
  select
    field1, field2, field3
  <include refid="someinclude">
    <property name="prefix" value="Some"/>
    <property name="include_target" value="sometable"/>
  </include>
</select>


————————CURD语法————————

接口类

package com.mybatislearn.mapper;

import com.mybatislearn.entity.Employee;

public interface EmployeeMapper {

    /**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    public Employee getEmployeeById(int id);

    /**
     * 增加员工信息
     * @param employee
     * @return
     */
    public Integer insertEmployee(Employee employee);


    /**
     * 修改员工信息
     * @param employee
     * @return
     */
    public boolean updateEmployee(Employee employee);

    /**
     * 删除员工信息
     * @param id
     * @return
     */
    public void deleteEmployee(Integer id);

}


实现类

package com.mybatislearn.test;

import com.mybatislearn.entity.Employee;
import com.mybatislearn.mapper.EmployeeMapper;
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.InputStream;

/**
 * @program: mybatis01
 * @description:mybatislearn
 * @author: mybatis
 * @create: 2021-08-11 17:07
 **/
public class TestMyBatis {
    @Test
    public void test()  throws Exception{
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession(true);
        try {

   				 //Mapper接口:获取Mapper接口的 代理实现类对象
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

            //1: 查询操作调用实际方法实现操作
            Employee employee = mapper.getEmployeeById(1);
            System.out.println(employee);


            //2: 添加操作
            Employee employee = new Employee();
            employee.setLastname("tom");
            employee.setEmail("tom@qq.com");
            employee.setGender("1");
            Integer rows = mapper.insertEmployee(employee);
            System.out.println(rows);


            //3:修改操作
            Employee employee = new Employee();
            employee.setLastname("tomm");
            employee.setEmail("tom@163.com");
            employee.setGender("1");
            employee.setId(9);
            boolean isupdate = mapper.updateEmployee(employee);
            System.out.println(isupdate);

            //4:删除操作
            Employee employee = new Employee();
            mapper.deleteEmployee(9);

        } finally {
            session.close();
        }
    }

}

mapper.xml 文件

<?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.mybatislearn.mapper.EmployeeMapper">
    <!--查询-->
    <select id="getEmployeeById" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{id}
    </select>

    <!--增加:useGeneratedKeys和keyProperty属性是设置自增长和自增长的字段的-->
    <insert id="insertEmployee" parameterType="com.mybatislearn.entity.Employee" useGeneratedKeys="true" keyProperty="id>
        INSERT INTO tbl_employee(lastname,email,gender) VALUES(#{lastname},#{email},#{gender})
    </insert>

    <!--修改-->
    <update id="updateEmployee" parameterType="com.mybatislearn.entity.Employee">
        UPDATE tbl_employee SET lastname=#{lastname},email=#{email},gender=#{gender} WHERE id=#{id}
    </update>

    <!--删除-->
    <!--删除方法的返回值是基本类型所以parameterType可以忽略不写-->
    <delete id="deleteEmployee" parameterType="java.lang.Integer">
        delete from tbl_employee where id=#{id}
    </delete>
</mapper>



表结构


在这里插入图片描述





————————mybatis参数传递方式及源码分析————————


一、参数传递方式

1) 单个普通类型参数

  • MyBatis可直接使用这个参数,不需要经过任何处理;
  • 因为只有一个参数,表达式参数名称(#{id})可以任意起名
    <select id="getEmployeeById" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{id}
    </select>


2) 多个参数

a)Mybatis 3.4版本

  • mybatis底层会将参数封装为map,map的key是固定值【0,1,2…或 param1,param2,param3,…】
	<select id="getEmployeeCondition" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{0} and lastname = #{1}
    </select><select id="getEmployeeCondition" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{param1} and lastname = #{param2}
    </select>


b)Mybatis3.5版本

  • mybatis底层会将这些参数封装为map,map的key是固定值【arg0,arg1,…或 param1,param2,param3,…】
    <select id="getEmployeeCondition" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{arg0} and lastname = #{arg1}
    </select><select id="getEmployeeCondition" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{param1} and lastname = #{param2}
    </select>


底层是key value值:

在这里插入图片描述

3) 命名参数

可以使用@Param注解,自定义参数名称,MyBatis会将这些参数封装进map中,key就是我们自己指定的名字;

在这里插入图片描述

接口类:


 /**
     * 根据多个条件查询
     * @param id
     * @return
     */
    public Employee getEmployeeCondition(@Param(value = "ids") Integer id, @Param(value = "lastnames") String lastname);


mapper.xml:
    <select id="getEmployeeCondition" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{ids} and lastname = #{lastnames}
    </select>


4) POJO

POJO(Plain Old Java Objects,普通的java对象);
当这些参数属于我们业务POJO时,可以直接传递POJO;

接口类:


    /**
     * 增加员工信息
     * @param employee
     * @return
     */
    public Integer insertEmployee(Employee employee);


mapper.xml:
    <!--增加-->
    <insert id="insertEmployee" parameterType="com.mybatislearn.entity.Employee" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO tbl_employee(lastname,email,gender) VALUES(#{lastname},#{email},#{gender})
    </insert>


5) Map

可以封装多个参数为map,直接传递;

接口类:


/**
     * 使用map传递参数
     * @param map
     * @return
     */
    public Employee getEmployeeMap(Map<String,Object> map);


mapper.xml:
    <select id="getEmployeeMap" parameterType="hashmap" resultType="com.mybatislearn.entity.Employee">
        select * from tbl_employee where id = #{id} and lastname = #{lastname}
    </select>


6) Collection/Array

会被MyBatis封装成一个map传入,Collection对应的key是collection,Array对应的key是array,如果确定是List集合,key还可以是list;


二、参数传递源码分析

以命名参数为例:

代码:
public Employee getEmployeeByIdAndLastName
(@Param("id")Integer id, @Param("lastName")String lastName);


源码:
public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
    } else {
      //new一个map集合 
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      //遍历传过来的map集合,获得key和value的值
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      //返回该map
      return param;
    }
  }


三、参数处理

  1. 参数位置支持的属性:

javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression

  1. 实际上通常被设置的是:可能为空的列名指定 jdbcType ,例如:

insert into orcl_employee(id,last_name,email,gender) values(employee_seq.nextval,#{lastName,jdbcType=NULL },#{email},#{gender})

四、参数的获取方式

  1. #{key}:预编译方式。获取参数的值,预编译到SQL中。安全
  2. ${key}:非预编译方式。获取参数的值,拼接到SQL中。有SQL注入问题。

五、查询返回不同类型

1) 查询单行数据返回单个对象

    public Employee getEmployeeById(int id);

    <select id="getEmployeeById" resultType="com.atguigu.entity.Employee" >
        select * from tbl_employee where id = #{id}
    </select>


2) 查询多行数据返回对象的集合

  public List<Employee> getAll();

    <select id="getAll" resultType="com.atguigu.entity.Employee">
        select * from tbl_employee
    </select>


3) 查询单行数据返回Map集合

如果直接返回map类型,要求只能返回一条记录

    public Map<String,Object> getAllMap(int id);

	<select id="getAllMap" resultType="hashmap">
        select * from tbl_employee where id=#{id}
    </select>


4) 查询多行数据返回Map集合

	public List<Map<String,Object>> getAllMapList();

	<select id="getAllMapList" resultType="hashmap">
         select * from tbl_employee
    </select>



5) 查询返回某个值

	public int getCount();

    <select id="getCount" resultType="int">
        select count(*) from tbl_employee
    </select>



————————ResultMap自定义映射————————


【前言】


resultType属性指定查询结果的返回值类型,来让MyBatis自动将查询结果集封装成我们希望的类型进行返回。

resultType属性非常有用,但在返回结果类型比较复杂的情况下却无能为力,为此,MyBatis在select元素中还为我们提供了另外一个resultMap属性,用于引用一个使用<resultMap/>元素定义好的自定义结果集映射规则。
<resultMap/>算是MyBatis中最重要、最强大的元素了,它可以让你比使用JDBC调用结果集省掉90%的代码,也可以让你做许多JDBC不支持的事。 事实上,编写类似的对复杂语句联合映射的代码也许会需要上千行。resultMap的设计就是简单语句不需要明确的结果映射,而很多复杂语句确实需要描述它们的关系。

自定义resultMap,实现高级结果集映射
id :用于完成主键值的映射
result :用于完成普通列的映射
association :一个复杂的类型关联;许多结果将包成这种类型
collection : 复杂类型的集

【正文】


一、单表查询


实际上,单表查询直接使用resultMap即可,没必要用resultMap自定义映射,这里仅做一个简单的演示。

接口类:

    /**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    public Employee getEmployeeResultMap(int id);

mapper:
    <!--根据id查询员工信息-->
    <select id="getEmployeeResultMap" resultMap="myMap1">
        select id,last_name from tbl_employee where id=#{id}
    </select>

    <resultMap id="myMap1" type="com.mybatislearn.entity.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
    </resultMap>


结果:

在这里插入图片描述


二、多表查询


现有部门表和员工表,表结构如下:
在这里插入图片描述

在这里插入图片描述


题目一: 根据员工id查询 员工信息 及 员工所在部门信息
方式一:使用 id & result 标签
  • id :用于完成主键值的映射
  • result :用于完成普通列的映射

实体类:

员工表:

public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private String gender;

    //部门对象属性
    private Dept dept;

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", gender='" + gender + '\'' +
                ", dept=" + dept +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}


部门表:

public class Dept {
    private Integer did;
    private String dname;

    //某个部门有很多个员工
    private List<Employee> list;

    @Override
    public String toString() {
        return "Dept{" +
                "did=" + did +
                ", dname='" + dname + '\'' +
                ", list=" + list +
                '}';
    }

    public Integer getDid() {
        return did;
    }

    public void setDid(Integer did) {
        this.did = did;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public List<Employee> getList() {
        return list;
    }

    public void setList(List<Employee> list) {
        this.list = list;
    }
}


接口类:

    /**
     * 根据id查询员工及其所属部门信息
     * @param id
     * @return
     */
    public Employee getEmployeeAndDept(int id);


mapper.xml:


	<!--根据id查询员工及其所属部门信息-->
    <select id="getEmployeeAndDept" resultMap="myEmpAndDept">
        SELECT * FROM tbl_dept d,tbl_employee e WHERE d.`did`=e.`did` AND e.`id`=#{id}
    </select>

	<!--自定义映射-->
    <resultMap id="myEmpAndDept" type="com.mybatislearn.entity.Employee">

        <!--员工表对应关系-->
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>

        <!--部门表对应关系-->
        <result column="did" property="dept.did"/>
        <result column="dname" property="dept.dname"/>

    </resultMap>


方式二:使用 association 标签
  • association :一个复杂的类型关联;许多结果将包成这种类型;
(1)association
<!--自定义映射-->
    <resultMap id="myEmpAndDept" type="com.mybatislearn.entity.Employee">

        <!--员工表对应关系-->
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>

        <!--部门表对应关系-->
        <association property="dept" javaType="com.mybatislearn.entity.Dept">
            <id column="did" property="did"/>
            <result column="dname" property="dname"/>
        </association>

    </resultMap>


(2)association 分步查询

分两步:
① 先通过员工的id查询员工信息;
② 再通过查询出来的员工信息中的外键(部门id)查询对应的部门信息;

接口类:

/**
     * 根据部门id查询部门信息
     * @param did
     * @return
     */
    public Dept getDeptById(int did);

    /**
     * 根据员工id查询员工信息
     * @param id
     * @return
     */
    public Employee getEmployeeAndDeptStep(int id);


mapper:

    <!--根据部门id查询部门信息-->
    <select id="getDeptById" resultType="com.mybatislearn.entity.Dept">
        select * from tbl_dept where did=#{did}
    </select>

    <!--根据员工id查询员工信息-->
    <select id="getEmployeeAndDeptStep" resultMap="myEmpAndDeptStep">
        select * from tbl_employee where id=#{id}
    </select>

    <resultMap id="myEmpAndDeptStep" type="com.mybatislearn.entity.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
        <!--员工所属部门
        select:查询员工所属部门的方法
        fetchType:是否延迟 lazy:延迟 eager:不延迟
        -->
        <association property="dept" select="com.mybatislearn.mapper.EmployeeMapper.getDeptById"
                     column="did" fetchType="eager">

        </association>
    </resultMap>


结果:

在这里插入图片描述


(3)association 分步查询 使用延迟加载


在分步查询的基础上,可以使用延迟加载来提升查询的效率,也称懒惰查询,现在一般都不用这个技术了,有更先进的技术如elasticsearch等其他来代替;
用法: 只需要在全局的Settings中进行如下的配置;并在mapper里fetchType="lazy"(属性改为lazy)
mybatis-config.xml:


    <settings>
        <!-- 开启延迟加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 设置加载的数据是按需还是全部 -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>


此时,如果值调用方法,不打印数据的话,就会只调用一个方法:

在这里插入图片描述

不打印结果:

在这里插入图片描述

打印结果:

在这里插入图片描述

说明:
延迟加载是只在用的时候才加载,可以在一定方面提升效率。

题目二:根据id查询 部门信息 和 该部门所有员工信息
方式三:使用 collection 标签
  • collection : 复杂类型的集;
(1)collection

实体类:

    //某个部门有很多个员工
    private List<Employee> emps;


接口:


    /**
     * 根据部门id查询 部门信息 和 部门所有员工信息
     * @param did
     * @return
     */
    public Dept getDeptAndEmpsById(int did);


mapper:

    <!--根据部门id查询 部门信息 和 部门所有员工信息-->
    <select id="getDeptAndEmpsById" resultMap="myDeptAndEmp">
        SELECT * FROM tbl_dept d LEFT JOIN tbl_employee e ON d.`did`=e.`did` WHERE d.`did`=#{did}
    </select>

    <resultMap id="myDeptAndEmp" type="com.mybatislearn.entity.Dept">
        <id column="did" property="did"/>
        <result column="dname" property="dname"/>
        <!--关联:一个部门中有很多员工-->
        <collection property="emps" ofType="com.mybatislearn.entity.Employee">
            <id column="id" property="id"/>
            <result column="last_name" property="lastName"/>
            <result column="email" property="email"/>
            <result column="gender" property="gender"/>
        </collection>
    </resultMap>


实现类:

	Dept dept = mapper.getDeptAndEmpsById(1);
	System.out.println(dept);


结果:

在这里插入图片描述


(2)collection 分步查询

分两步:
① 先通过部门的id查询部门信息
② 再通过部门id作为员工的外键查询对应的部门信息

mapper

	<select id="getDeptAndEmpsByIdStep" resultMap="myDeptAndEmpsStep">
	 	select id ,dept_name  from tbl_dept where id = #{id}
	 </select>

	 <resultMap type="com.mybatislearn.mybatis.beans.Department" id="myDeptAndEmpsStep">
	 	<id column="id" property="id"/>
	 	<result column="dept_name" property="departmentName"/>
	 	<collection property="emps"
	 			select="com.mybatislearn.mybatis.dao.EmployeeMapper.getEmpsByDid"
	 			column="did">
	 	</collection>
 	</resultMap>


(3)collection 分步查询 使用延迟加载

用法同association:
在全局的Settings中进行配置;
并在mapper里fetchType="lazy"



三、拓展


(1)分步查询多列值的传递


如果分步查询时,需要传递给调用的查询中多个参数,则需要将多个参数封装成Map来进行传递,语法如下: {k1=v1, k2=v2....};
在所调用的查询方,取值时就要参考Map的取值方式,需要严格的按照封装map时所用的key来取值;

(2) association 或 collection的 fetchType属性


在<association> 和<collection>标签中都可以设置 fetchType 属性,指定本次查询是否要使用延迟加载;
默认为fetchType=”lazy” ,如果本次的查询不想使用延迟加载,则可设置为fetchType=”eager”;
fetchType 属性可以灵活的设置查询是否需要使用延迟加载,而不需要因为某个查询不想使用延迟加载将全局的延迟加载设置关闭;





————————动态sql————————


【前言】


前面我们学习的都是一些固定的SQL语句,一个方法对应一条语句,这样导致的结果就是会写很多SQL,导致代码冗余,效率不高等问题,今天我们来学习一下动态SQL的用法。

【正文】


一、定义


动态SQL:即SQL是不固定的,可以根据不同的条件对应拼接出不同的SQL语句;
mybatis使用动态sql实现多条件查询功能。

二、简介

  1. 动态 SQL是MyBatis强大特性之一;极大的简化我们拼装SQL的操作;
  2. 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似;
  3. MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作;
    • OGNL的标签:
    if
    choose (when, otherwise)
    trim (where, set)
    foreach
    
  4. OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。 类似EL,SpEL等


访问对象属性:person.name
调用方法:person.getName()
调用静态属性/方法:@java.lang.Math@PI @java.util.UUID@randomUUID()
调用构造方法:new com.atguigu.bean.Person(‘admin’).name
运算符:+,-*,/,%
逻辑运算符:in,not in,>,>=,<,<=,==,!=

注意:xml中特殊符号如”,>,<等这些都需要使用转义字符

三、OGNL 标签详解


☆(1)if where


If用于完成简单的判断.
Where用于解决SQL语句中where关键字以及条件中第一个and或者or的问题

接口:

/**
     * 实现根据员工名称 和 员工邮箱 进行多条件查询
     * @param employee
     * @return
     */
    public Employee getEmployeeConditions(Employee employee);

mapper:
    <!--实现根据员工名称 和 员工邮箱 进行多条件查询-->
    <select id="getEmployeeConditions" parameterType="com.mybatislearn.entity.Employee" resultType="com.mybatislearn.entity.Employee">
        SELECT * FROM tbl_employee emp
        <where>
            <!--判断条件值是否为空-->
            <if test="email != null and email.trim() != ''">
                <!--拼接SQL语句-->
                and email = #{email}
            </if>
            <if test="lastName != null and lastName.trim() != ''">
                and last_name like CONCAT(CONCAT('%',#{lastName},'%'))
            </if>
         </where>
</select>


实现类:
            Employee employee = new Employee();
            employee.setEmail("55555");
            employee.setLastName("");
            Employee emp = mapper.getEmployeeConditions(employee);
            System.out.println(emp);


结果:

在这里插入图片描述

注意:
  • CONCAT函数是mysql里拼接字符串的函数;
  • 常用转义符:


-

(2)trim
  • 前后缀操作
  • Trim 可以在条件判断完的SQL语句前后 添加或者去掉指定的字符
    prefix: 添加前缀
    prefixOverrides: 去掉前缀
    suffix: 添加后缀
    suffixOverrides: 去掉后缀


接口:
ublic Employee getEmpsByConditionTrim(Employee employee);


mapper:
    <select id="getEmpsByConditionTrim" resultType="com.mybatislearn.entity.Employee">
        select id , last_name ,email  , gender
        from tbl_employee
        <trim prefix="where" suffixOverrides="and">
            <if test="id!=null">
                id = #{id} and
            </if>
            <if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
                last_name = #{lastName} and
            </if>
            <if test="email!=null and email.trim()!=''">
                email = #{email} and
            </if>
            <if test="&quot;m&quot;.equals(gender) or &quot;f&quot;.equals(gender)">
                gender = #{gender}
            </if>
        </trim>
    </select>


实现类:


            Employee employee = new Employee();
            employee.setId(4);
            employee.setLastName("张rose");
            employee.setEmail("55555");

            Employee emp = mapper.getEmpsByConditionTrim(employee);
            System.out.println(emp);


结果:

在这里插入图片描述

(3)set
  • 是否去掉逗号;
  • 如果条件不满足,逗号会自动去掉;
  • set 主要是用于解决修改操作中SQL语句中可能多出逗号的问题;
<update id="updateEmpByConditionSet">
		update  tbl_employee
		<set>
			<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
				 last_name = #{lastName},
			</if>
			<if test="email!=null and email.trim()!=''">
				 email = #{email} ,
			</if>
			<if test="&quot;m&quot;.equals(gender) or &quot;f&quot;.equals(gender)">
				gender = #{gender}
			</if>
		</set>
		 where id =#{id}
	</update>


(4)choose(when、otherwise)
  • 选择标签;
  • choose 主要是用于分支判断,类似于java中的switch case,只会满足所有分支中的一个;
<select id="getEmpsByConditionChoose" resultType="com.mybatislearn.entity.Employee">
		select id ,last_name, email,gender from tbl_employee
		<where>
			<choose>
				<when test="id!=null">
					id = #{id}
				</when>
				<when test="lastName!=null">
					last_name = #{lastName}
				</when>
				<when test="email!=null">
					email = #{email}
				</when>
				<otherwise>
					 gender = 'm'
				</otherwise>
			</choose>
		</where>
</select>


☆(5)foreach


foreach 主要用于循环迭代
      collection: 要迭代的集合
      item: 当前从集合中迭代出的元素
      open: 开始字符
      close:结束字符
      separator: 元素与元素之间的分隔符
index:
     迭代的是List集合: index表示的当前元素的下标
     迭代的是Map集合: index表示的当前元素的key
    使用in查询

接口:

    /**
     * 使用in查询
     * @param ids
     * @return
     */
    public List<Employee> getEmployeeIn(@Param(value = "ids") List<Integer> ids);

mapper:


    <!--使用in查询,演示foreach-->
    <select id="getEmployeeIn" resultType="com.mybatislearn.entity.Employee" parameterType="list">
        SELECT * FROM tbl_employee WHERE id IN
        <!--使用foreach遍历参数list,获取集合每个id值,把id放到括号,并且使用逗号隔开 (1,2,3)
            collection: 要遍历集合名称
            item: 每次遍历出来内容
            open: 遍历出来值放地方开始符号
            close:遍历出来值放地方结束符号
            separator: 每次遍历出来内容使用什么符号分割
            eg:(1,2,3)
        -->
        <foreach collection="ids" item="curr_id" open="(" close=")" separator=",">
            #{curr_id}
        </foreach>
    </select>


☆(6)sql
  1. sql 标签是用于抽取可重用的sql片段,将相同的,使用频繁的SQL片段抽取出来,单独定义,方便多次引用;
  2. 抽取SQL:
<sql id="selectSQL">
		select id , last_name, email ,gender from tbl_employee
</sql>


  1. 引用SQL:
<include refid="selectSQL"></include>


————————  一级缓存 二级缓存详解  ————————


【前言】


MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存。
默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。

【正文】


一、一级缓存


(一)简介
  • mybatis的一级缓存是默认打开的;
  • 一级缓存使用范围:sqlsession;
  • 一级缓存数据是使用map集合封装的:
  • 同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中:
    key: hashCode+查询的SqlId+编写的sql查询语句+参数
    value:查询结果

(二)使用


接口:


    /**
     * 根据部门id查询部门信息
     * @param did
     * @return
     */
    public Dept getDeptById(int did);


mapper:


 <!--根据部门id查询部门信息-->
    <select id="getDeptById" resultType="com.mybatislearn.entity.Dept">
    select * from tbl_dept where did=#{did}
    </select>


实现类:
public class TestMyBatis {
    @Test
    public void test()  throws Exception{
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession(true);
        try {

//     Mapper接口:获取Mapper接口的 代理实现类对象
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

            Dept dept = mapper.getDeptById(1);
            System.out.println(dept);

            Dept dept1 = mapper.getDeptById(2);
            System.out.println(dept1);

        } finally {
            session.close();
        }
    }

}

  • 第一次查:
            Dept dept = mapper.getDeptById(1);
            System.out.println(dept);


第一次查的结果:

在这里插入图片描述

  • 第二次查:

            Dept dept = mapper.getDeptById(1);
            System.out.println(dept);

            Dept dept1 = mapper.getDeptById(1);
            System.out.println(dept1);


  • 第二次查的结果:

在这里插入图片描述

  • 很显然,第二次查的时候,没有再查询数据库,而是直接从一级缓存里查到了结果,并进行了输出。

  • 查不同的数据

            Dept dept = mapper.getDeptById(1);
            System.out.println(dept);

            Dept dept1 = mapper.getDeptById(2);
            System.out.println(dept1);


  • 查不同的数据的结果

在这里插入图片描述



这里如果查不同的数据,从缓存中查不到,因此是执行了两次数据查询的。

(三)失效情况


不同的SqlSession对应不同的一级缓存(SqlSession session = sqlSessionFactory.openSession(true);)
同一个SqlSession但是查询条件不同(增删改查)
同一个SqlSession两次查询期间执行了任何一次增删改操作
同一个SqlSession两次查询期间手动清空了缓存(session.clearCache();)

二、二级缓存


(一)简介


mybatis里面二级缓存默认没有打开的,需要手动打开,并且二级缓存使用需要依赖第三方插件
二级缓存使用范围namespace访问
二级缓存在 SqlSession 关闭或提交之后才会生效

(二)配置


第一步:导入jar包


在这里插入图片描述在这里插入图片描述


第二步:在mybatis全局配置文件开启二级缓存

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

在这里插入图片描述


第三步:创建第三方缓存插件配置文件(ehcache)

ehcache.xml:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 -->
    <diskStore path="E:\mybatis-learn\ehcache" />

    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>


第四步 在映射配置文件中添加ehcache缓存支持配置

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

mapper.xml:

在这里插入图片描述

第五步 查询对应实体类实现Serializable接口
第六步 在查询select标签添加使用缓存属性

useCache="true"


    <select id="getDeptById" resultType="com.mybatislearn.entity.Dept" useCache="true">
    select * from tbl_dept where did=#{did}
    </select>


补充:
  1. 二级缓存(second level cache),全局作用域缓存
  2. 二级缓存默认不开启,需要手动配置
  3. MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
  4. 二级缓存在 SqlSession 关闭或提交之后才会生效
  5. 二级缓存使用的步骤:
    1. ① 全局配置文件中开启二级缓存
      ② 需要使用二级缓存的映射文件处使用cache配置缓存
      ③ 注意:POJO需要实现Serializable接口
  6. 二级缓存相关的属性
    ① eviction=“FIFO”:缓存回收策略:
  • LRU – 最近最少使用的:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 默认的是 LRU。


② flushInterval:刷新间隔,单位毫秒

默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新


③ size:引用数目,正整数

代表缓存最多可以存储多少个对象,太大容易导致内存溢出


④ readOnly:只读,true/false

true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。


false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。


(三)使用


实现类:


public class TestMyBatis {
    @Test
    public void test()  throws Exception{
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session1 = sqlSessionFactory.openSession(true);
        SqlSession session2 = sqlSessionFactory.openSession(true);
        try {

//     Mapper接口:获取Mapper接口的 代理实现类对象
            EmployeeMapper mapper1 = session1.getMapper(EmployeeMapper.class);
            EmployeeMapper mapper2 = session2.getMapper(EmployeeMapper.class);


            Dept dept1 = mapper1.getDeptById(1);
            System.out.println("dept1"+dept1);

            //二级缓存在sqlsession关闭或提交后才会生效
            session1.close();

            Dept dept2 = mapper2.getDeptById(1);
            System.out.println("dept2"+dept2);
        } finally {
            session2.close();
        }
    }

}
结果:

在这里插入图片描述


缓存的相关属性设置

  1. 全局setting的cacheEnable: 配置二级缓存的开关,一级缓存一直是打开的。
  2. select标签的useCache属性: 配置这个select是否使用二级缓存。一级缓存一直是使用的
  3. sql标签的flushCache属性: 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。 查询默认 flushCache=false。
  4. sqlSession.clearCache():只是用来清除一级缓存。



————————mybatis支持驼峰式命名方式配置 ————————

当数据库中字段名类似last_name时,可自动映射至lastName形式

	<settings>
        <setting name="mapUnderscoreToCamelCase"value="true"/>
    </settings>




————————插件篇——逆向工程(代码生成器)————————

【前言】


下面两篇博客,将分别介绍一下mybatis的两个插件:逆向工程和分页插件。

【正文】


一、简介


MyBatis Generator: 简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删改查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sql的定义需要我们手工编写;

官方文档

官方工程地址

二、配置


第一步:导入jar包


mybatis-generator-core-1.3.2.jar

在这里插入图片描述


第二步:创建配置文
  • 一般在项目的根目录下创建

在这里插入图片描述

<?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>
    <!--
            targetRuntime: 执行生成的逆向工程的版本
                    MyBatis3Simple: 生成基本的CRUD
                    MyBatis3: 生成带条件的CRUD
     -->
    <context id="DB2Tables" targetRuntime="MyBatis3">

        <!--数据库信息配置-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/test_mybatis"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <!-- 实体类的生成策略-->
        <javaModelGenerator targetPackage="com.mybatislearn.entity" targetProject=".\src">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.mybatislearn.mapper"  targetProject=".\src">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.mybatislearn.mapper"  targetProject=".\src">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!-- 逆向分析的表:表名称和对应表的实体类 -->
        <table tableName="tbl_employee" domainObjectName="Employee"></table>
    </context>
</generatorConfiguration>


第三步:编写代码


public class TestMyBatis {

    @Test
    public void testMBG() throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;

        //默认从根目录开始找,如果不是在根目录需要补全路径名称
        File configFile = new File("mbg.xml");

        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback, warnings);
        myBatisGenerator.generate(null);
	}
}


结果:


成功生成

在这里插入图片描述


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


第四步:代码测试

QBC风格
使用方法来实现条件查询

//2 使用逆向工程代码实现条件查询
// 根据名称 和 邮箱
//2.1 创建EmployeeExample对象
EmployeeExample example = new EmployeeExample();
//2.2 创建封装条件对象
EmployeeExample.Criteria criteria = example.createCriteria();
//2.3 拼接条件
// last_name='lucy'
//criteria.andLastNameEqualTo("lucy");
// last_name like '张%'
criteria.andLastNameLike("张%");
// email = '44444'
criteria.andEmailEqualTo("44444");

List<Employee> list = mapper.selectByExample(example);
System.out.println(list);





————————插件篇——PageHelper分页————————


一、简介

  1. PageHelper是MyBatis中的一个第三方分页插件
  2. 对照官方文档的说明,快速的使用插件

二、使用步骤

1) 导入jar包

pagehelper-x.x.x.jar 和 jsqlparser-0.9.5.jar

和 两个日志jar包

在这里插入图片描述

2) 在MyBatis全局配置文件中配置分页插件

<plugins>
	<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>


在这里插入图片描述

3) 编写代码:Page对象实现分页

(1)Page对象实现分页
  • 在查询之前通过PageHelper.startPage(页码,条数)设置分页信息,该方法返回Page对象
@Test
    public void testPage()  throws Exception{
        String resource = "mybatis-config.xml";
        //获取src下面配置文件输入流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建SqlSession工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂创建SQLSession对象,参数值true表示事务自动提交
        SqlSession session = sqlSessionFactory.openSession(true);
        try {

            //Mapper接口:获取Mapper接口的 代理实现类对象
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

            //1:创建page对象
            //startPage方法传递2个参数:第一个是当前页,第二个是每页显示记录数
            Page<Object> page = PageHelper.startPage(1,3);

            //2:调用方法查询表中所有记录
            List<Employee> list = mapper.selectByExample(null);

            for (Employee emp:list){
                System.out.println(emp);
            }
            //3:实现分页查询之后,pageHelper把分页信息封装page对象里面
            int pageNum = page.getPageNum(); //当前页
            int pages = page.getPages();//总页数
            int pageSize = page.getPageSize(); //每页显示记录数
            long total = page.getTotal(); //总记录数

            System.out.println(pageNum);
            System.out.println(pages);
            System.out.println(pageSize);
            System.out.println(total);

        } finally {
            session.close();
        }
    }

结果:

在这里插入图片描述


(2)PageInfo对象实现分页
  • 在查询完数据后,使用PageInfo对象封装查询结果,可以获取更详细的分页信息以及可以完成分页逻辑
@Test
    public void testPageInfo()  throws Exception{
        //配置文件信息
        String resource = "mybatis-config.xml";
        //获取src下面配置文件输入流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建SqlSession工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂创建sqlSession对象,参数值true表示事务自动提交
        SqlSession session = sqlSessionFactory.openSession(true);

        try {
            EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

           //1 2 创建page对象,查询所有记录
            Page<Object> page = PageHelper.startPage(3, 3);
            List<Employee> list = mapper.selectByExample(null);

            //3 把分页查询之后所有信息封装到pageInfo对象里面
            //PageInfo(List<T> list, int navigatePages)
            // 第一个参数分页数据list集合
            //第二个参数 页码数显示多少个
            PageInfo<Employee> info = new PageInfo<>(list,2);

            System.out.println("当前页: " + info.getPageNum());
            System.out.println("总页码: " + info.getPages());
            System.out.println("总条数: " + info.getTotal());
            System.out.println("每页显示的条数: " + info.getPageSize());

            System.out.println("是否是第一页: " + info.isIsFirstPage());
            System.out.println("是否是最后一页: " + info.isIsLastPage());
            System.out.println("是否有上一页: " + info.isHasPreviousPage());
            System.out.println("是否有下一页: " + info.isHasNextPage());

            System.out.println("============分页逻辑===============");
            int[] nums = info.getNavigatepageNums(); //所有页面数
            for(int num:nums) {
                System.out.println(num);
            }
        } finally {
            session.close();
        }
    }

结果:

在这里插入图片描述






mybatis 动态标签详解


if 标签


if标签中有一个test属性,test属性值是一个符合OGNL要求的判断表达式,表达式的结果可以使true或者false, 除此之外所有的非0值都为true

(1)、数字类型


1.1 例如:  如果参数为数字类型的时候没有特俗需求的情况只需要判断是否为null即可。
<if test="id != null"></if>


 1.2 例如:如果有特俗需求,例如判断是否大于某个数的时候才行。只需要加上对应的条件判断即可。
<if test='id != null and id > 28'></if>



 1.3 例如:mybatis对于这种大于小于等等还有另一种形式。

<if test='id != null and id gt 28'></if>
<if test='id != null and id > 28'></if> 这两种一样

<if test='id != null and id gte 28'></if>
<if test='id != null and id >= 28'></if> 这两种一样

<if test='id != null and id lt 28'></if> 正常
<if test='id != null and id < 28'></if> 报错

<if test='id != null and id lte 28'></if> 正常
<if test='id != null and id <= 28'></if> 报错



对应关系:

gt          对应             >

gte         对应              >=

lt           对应             <(会报错  相关联的 "test" 属性值不能包含 '<' 字符)

lte          对应             <=(会报错  相关联的 "test" 属性值不能包含 '<=' 字符)


(2) 字符串类型


2.1 例如: 如果不需要过滤空串的情况 仅仅判断null即可

<if test="username != null"></if>


2.2 例如:如果需要过滤空串,添加空串判断即可  不支持 &&和 || ,所以这里用 and  or 来做逻辑与或的判断

<if test="username != null and '' != username"></if> 或者 <if test="username != null and ''  neq username"></if>


2.3 例如:如果判断字符串是否已某个特俗字符开头,结尾等。直接调用String的对应方法即可

    <if test="username != null and username.indexOf('ji') == 0"> </if> <!-- 是否以什么开头 -->
    <if test="username != null and username.indexOf('ji') >= 0"> </if> <!-- 是否包含某字符 -->
    <if test="username != null and username.lastIndexOf('ji') > 0"></if>  <!-- 是否以什么结尾 -->


2.4 例如: 是否是某个特定字符串,某些业务有此需要。

<if test="username != null and 'hello' == username"></if> 或者<if test="username != null and 'hello' eq username"></if>

注意:

     <if test="username != null and 'hello' == username"></if>这种形式的写法在参数类型是字符串的时候是没有问题的,

但是参数类型为非字符串类型的时候就需要写成 <if test="username != null and 'hello'.toString() == username.toString()"></if>

仅仅写成<if test="username != null and 'hello'.toString() == username"></if>也会有很大可能会挂。

也许你会说非字符串的为什么要写成这样。这就要看特殊需要了。








对应关系:

eq                  对应                ==

neq               对应                 !=


 3 判断list是否为空

if条件判断可以直接调用对象自身的方法进行逻辑判断,所以list判空。可以调用.size()>0或者.isEmpty()

例如:<if test="userList != null and userList.isNotEmpty()"></if> , <if test="userList != null and userList.size()>0"></if>


4 map参数同同理  取值的话 map.key(map中的key名字)即可


where标签


标签会进行自动判断, 如果任何条件都不成立,那么就在sql语句里就不会出现where关键字
如果有任何条件成立,会自动去掉多出来的 and 或者 or。(就不需要我们追加1=1之类的入侵性代码了)

用法:
<select id="listProduct" resultType="Product">
    select * from product_
    <where>
        <if test="name!=null">
            and name like concat('%',#{name},'%')
        </if>
        <if test="price!=null">
            and price > #{price}
        </if>
    </where>
</select>


set标签


与where标签类似的,在update语句里也会碰到多个字段相关的问题。 在这种情况下,就可以使用set标签。 其效果与where标签类似,有数据的时候才进行设置。 set 元素可以用于动态包含需要更新的列,忽略其它不更新的列,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号。


用法:

<update id="updateProduct" parameterType="Product" >
    update product_
    <set>
        <if test="name != null">name=#{name},</if>
        <if test="price != null">price=#{price}</if>
    </set>
     where id=#{id}
</update>


trim标签


trim 有四个参数,分别是:
prefix:前缀(以什么开头), 
prefixoverride:去掉第一个(比如 “and”或者是“or”)
suffix:后缀(以什么结尾)
suffixoverride:去掉最后标记的字符(比如“,”)


用法:
<select id="listProduct" resultType="Product">
    select *from product_
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="name!=null">
            and name like concat('%',#{name},'%')
        </if>
        <if test="price!=null and price!=0">
            and price > #{price}
        </if>
    </trim>
</select>

trim 用来定制想要的功能,比如where标签就可以用trim 来替换

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


<update id="updateProduct" parameterType="Product" >
    update product_
    <trim prefix="SET" suffixOverrides=",">
        <if test="name != null">name=#{name},</if>
        <if test="price != null">price=#{price},</if>
    </trim>
     where id=#{id}
</update>

set标签就可以用trim来替换 ,运行set标签中的代码,其效果是一样的。
<trim prefix="SET" suffixOverrides=",">
  ...
</trim>


choose when otherwise 标签


有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。MyBatis提供了choose 元素,按顺序判断when中的条件出否成立,如果有一个成立,则choose结束。当choose中所有when的条件都不满则时,则执行 otherwise中的sql。类似于Java 的switch 语句,choose为switch,when为case,otherwise则为default。if是与(and)的关系,而choose是或(or)的关系


<select id="getUserList" resultType="com.it.bean.User" parameterType="com.it.bean.User">
    SELECT <include refid="resultParam"></include> FROM User u
    <where>
        <choose>
            <when test="username !=null and username != ''">
                u.username LIKE CONCAT(CONCAT('%', #{username}),'%')
            </when >
            <when test="sex != null">
                AND u.sex = #{sex}
            </when >
            <when test="birthday != null ">
                AND u.birthday = #{birthday}
            </when >
            <otherwise>
 	            AND u.age = #{age}
            </otherwise>
        </choose>
    </where>
</select>


foreach标签


foreach标签通常用于in 这样的语法里。
collection :collection属性的值有三个分别是list、array、map三种,分别对应的参数类型为:List、数组、map集合,我在上面传的参数为数组,所以值为array

item : 表示在迭代过程中每一个元素的别名

index :表示在迭代过程中每次迭代到的位置(下标)

open :前缀

close :后缀

separator :分隔符,表示迭代时每个元素之间以什么分隔


<select id="listProduct" resultType="Product">
      SELECT * FROM product_
        WHERE ID in
            <foreach item="item" index="index" collection="list"
                open="(" separator="," close=")">
                #{item}
            </foreach>
</select>


bind标签


bind标签中,value对应传入实体类的某个字段,name属性既给对应字段取的变量名。在value属性中可以使用字符串拼接等特殊处理。


用法:
    <select id="listProduct" resultType="Product">
        <bind name="likename" value="'%' + name + '%'" />
        select * from   product_  where name like #{likename}
    </select>


sql片段标签


通过该标签可定义能复用的sql语句片段,在执行sql语句标签中直接引用即可。
这样既可以提高编码效率,还能有效简化代码,提高可读性。 sql标签 用来封装SQL语句, include 标签来调用。

用法:
<!--定义sql片段-->
<sql id="orderAndItem">    o.order_id,o.cid,o.address,o.create_date,o.orderitem_id,i.orderitem_id,i.product_id,i.count
</sql>
<select id="findOrderAndItemsByOid" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
<!--引用sql片段-->
    <include refid="orderAndItem" />
    from ordertable o
    join orderitem i on o.orderitem_id = i.orderitem_id
    where o.order_id = #{orderId}
</select>












posted @ 2024-03-15 10:24  一品堂.技术学习笔记  阅读(39)  评论(0编辑  收藏  举报