Mybatis 教程 第二节
代理模式(推荐)
在dao层只需要写接口,不需要实现,通过getMapper的方法可以获取到接口的代理对象,从而操作数据库。
满足的条件
-
1.映射文件中的namespace必须等于接口的全限定类名
-
2.接口中定义的抽象方法的方法名必须和映射文件中的sqlid一致
-
3.接口中抽象方法的输入参数的个数最多只能有一个(注解除外),并且输入参数的类型必须和映射文件中parameterType的类型一致
-
4.接口中方法的返回结果的类型或泛型必须是resultType(或者是resultMap中的type类型)
-
5.映射文件应该和接口文件满足同包同名的要求
在查询时,mybatis会自动根据方法的返回结果类型自动选择selectOne或selectList来执行。如果方法的返回结果为实体类型或简单类型,调用selectOne。如果方法的返回结果为LIst集合,自动调用selectList
mybatis调试
因为myabtis的sql语句写在映射文件中,没有办法打断点来调试sql语句,mybatis通过提供日志来实现sql的调试。
log4j
- 添加依赖
- commons-logging.jar
- log4j.jar
- 添加日志配置文件
- log4j.properties
log4j2
-
添加依赖
- commons-logging
- log4j-core
- log4j-api
-
添加配置文件
- log4j2.xml
logback
有部分开发项目使用了早期的Log4j,针对log4j和logback做了slf4j的适配模式
配置文件详解
全局配置文件
properties*
<!--
1.将外部资源加载到mybatis中,可以使用ognl表达式 ${}
resource基于classpath来查找文件
url:可以存放网络上配置文件的地址 OSS
2.mybatis内部定义常用的变量
内部定义定义的变量和外部资源加载存在优先级的问题???
内部先加载,外部后加载,同名的会覆盖
-->
<properties resource="jdbc.properties">
<property name="jdbc.driver" value="aaaaaa"/>
<property name="jdbc.pwd" value="123456"/>
</properties>
settings*
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods )。 | true | false | false (在 3.4.1 及之前的版本中默认为 true) |
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 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
defaultFetchSize | 为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。 | 任意正整数 | 未设置 (null) |
defaultResultSetType | 指定语句默认的滚动策略。(新增于 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置) | 未设置 (null) |
safeRowBoundsEnabled | 是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。 | true | false | False |
safeResultHandlerEnabled | 是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。 | true | false | True |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION | STATEMENT | SESSION |
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 | false | false |
returnInstanceForEmptyRow | 当返回行的所有列都是空时,MyBatis默认返回 null 。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2) | true | false | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
proxyFactory | 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 | CGLIB (3.5.10 起废弃) | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的实现 | 自定义 VFS 的实现的类全限定名,以逗号分隔。 | 未设置 |
useActualParamName | 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1) | true | false | true |
configurationFactory | 指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3) | 一个类型别名或完全限定类名。 | 未设置 |
shrinkWhitespacesInSql | 从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5) | true | false | false |
defaultSqlProviderType | 指定一个拥有 provider 方法的 sql provider 类 (新增于 3.5.6). 这个类适用于指定 sql provider 注解上的type (或 value ) 属性(当这些属性在注解中被忽略时)。 (e.g. @SelectProvider ) | 类型别名或者全限定名 | 未设置 |
nullableOnForEach | 为 ‘foreach’ 标签的 ‘nullable’ 属性指定默认值。(新增于 3.5.9) | true | false | false |
argNameBasedConstructorAutoMapping | 当应用构造器自动映射时,参数名称被用来搜索要映射的列,而不再依赖列的顺序。(新增于 3.5.10) | true | false | false |
typealiases*
内别名
基本数据类型的别名"_基本数据类型" int _int
包装数据类型的别名是他对应的基本数据类型 java.lang.Integer int
java.lang.String string
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_char (since 3.5.10) | char |
_character (since 3.5.10) | char |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
char (since 3.5.10) | Character |
character (since 3.5.10) | Character |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
biginteger | BigInteger |
object | Object |
date[] | Date[] |
decimal[] | BigDecimal[] |
bigdecimal[] | BigDecimal[] |
biginteger[] | BigInteger[] |
object[] | Object[] |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
自定义别名
- 单个别名设置
700多张表,700个pojo
<typeAliases>
<!-- 单个别名设置
type:原来的数据类型
aliase:别名
-->
<typeAlias type="com.xxx.pojo.User" alias="user"/>
</typeAliases>
- 批量别名设置(推荐)
<typeAliases>
<!--
批量别名设置
name: 需要设置别名的包的包名
别名就是类名,不区分大小写
-->
<package name="com.xxx.pojo" />
</typeAliases>
typeHandlers
自定义类型转换器。mysql中的varchar会自动封装到String中
- 自定义类型转换器
java类,需要指定如何进行类型转换
你可以重写已有的类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 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>
objectFactory
配置对象工厂
selectOne 查询语句返回实体对象,采用反射,通过工厂模式调用反射生成对象
- 定义工厂
- 配置工厂
objectWrapperFactory
包装对象工厂
reflectorFactory
反射工厂
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 接口,并指定想要拦截的方法签名即可。
除了用插件来修改 MyBatis 核心行为以外,还可以通过完全覆盖配置类来达到目的。只需继承配置类后覆盖其中的某个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会极大影响 MyBatis 的行为,务请慎之又慎。
<!-- mybatis-config.xml -->
<plugins>
<!--多个拦截器的配置
interceptor:拦截器的全限定类名
-->
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<!-- 拦截器需要的参数-->
<property name="someProperty" value="100"/>
</plugin>
</plugins>
environments
环境的配置
databaseIdProvider
多数据库厂商标识。hibernate,根据不同的数据库,hql语句会自动翻译成对应的数据库的语句。1次代码可以适应不同的数据库。
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<select id="selectUsers" resultType="UsEr" databaseId="sqlserver">
select * from tb_user
</select>
<select id="selectUsers" resultType="UsEr">
select * from tb_user
</select>
mappers*
加载映射文件
- 单个映射文件加载
- 批量映射文件加载(推荐)
<!-- 加载映射文件 -->
<mappers>
<!-- resource代表classpath下xml的路径
<mapper resource="com/xxx/mapper/UserMapper.xml"/>
-->
<!--
url:绝对路径
<mapper url="file:///D:\eclipseworkspace202301\mybatis1\src\com\xxx\mapper\UserMapper.xml"/>
-->
<!--
接口的全限定类名,要求:映射文件和接口文件必须满足同包同名的要求
<mapper class="com.xxx.mapper.UserMapper"/>
-->
<!-- 批量映射文件加载
满足同包同名的要求
-->
<package name="com.xxx.mapper"/>
</mappers>
映射文件
所有的标签和属性,操作数据库表
mapper
- namespace 命名空间
接口的全限定类名
包名—当Java类名重复时,包名不同可以存在
sqlId不允许重复
deleteById
- insert标签
只能执行insert语句
id:必选 sqlID,保证在同一个映射文件中不能重复(指定了databaseId时可以重复)
parameterType:(选填)指定输入参数的类型,值:权限定类名也可以是别名
parameterMap:(过时)
不允许有结果映射:但是该标签对应的方法可以有整形的返回结果 (影响行数)
订单应该如何设计???
订单主表:
create table tb_order_main( id int not null primary key auto_increment, order_no varchar(20), user_id int, flag int, total int );
订单明细表:
create table tb_order_detail( id int not null primary key auto_increment, main_id int, goods_id int, num int, price int, total int )
订单保存:先插入主表 再明细
last_insert_id()
用于获取最新插入的自增id的值,要求必须和insert使用相同的connenection
用于获取自增主键的值
方式1:
<insert id="insertMain" parameterType="orderMain"> <!-- 在插入时为输入参数的主键对应的属性赋值 在selectkey中执行的语句和insert语句使用了相同的connection keyColumn:指定selectKey中作为查询结果的列名 keyProperty:输入参数中需要赋值的属性的属性名 order: BEFORE:selectkey中的语句在insert语句执行前执行 oracle 在insert语句执行前通过rowid获取到自增主键的值 AFTER:selectkey中的语句在insert语句执行后执行 mysql resultType:指定selectkey的返回结果类型 --> <selectKey keyColumn="id1" keyProperty="id" order="AFTER" resultType="int"> select last_insert_id() as id1 </selectKey> insert into tb_order_main(order_no,user_id,flag,total) values(#{orderNo},#{userId},#{flag},#{total}) </insert>
方式2:
<setting name="useGeneratedKeys" value="true"/>
<!-- keyColumn:指定当前isnert语句中的自增主键的列的列名 keyProperty:指定输入参数中用于存储自增主键值的属性的属性名 --> <insert id="insertMain" parameterType="orderMain" keyColumn="id" keyProperty="id" useGeneratedKeys="true"> insert into tb_order_main(order_no,user_id,flag,total) values(#{orderNo},#{userId},#{flag},#{total}) </insert>
- update
执行执行update语句
id:必选 sqlID,保证在同一个映射文件中不能重复(指定了databaseId时可以重复)
parameterType:(选填)指定输入参数的类型,值:权限定类名也可以是别名
parameterMap:(过时)
不允许有结果映射:但是该标签对应的方法可以有整形的返回结果 (影响行数)
- delete
只能执行delete语句
id:必选 sqlID,保证在同一个映射文件中不能重复(指定了databaseId时可以重复)
parameterType:(选填)指定输入参数的类型,值:权限定类名也可以是别名
parameterMap:(过时)
不允许有结果映射:但是该标签对应的方法可以有整形的返回结果 (影响行数)
- select
只能执行select语句
id:必选 sqlID,保证在同一个映射文件中不能重复(指定了databaseId时可以重复)
parameterType:(选填)指定输入参数的类型,值:权限定类名也可以是别名
parameterMap:(过时)
必须有结果映射
*resultType: 返回结果的类型或返回结果的集合的泛型
用于同名映射,如果查询结果的列名和属性名完全不一致,返回null,LIst。如果查询结果的列名和属性名有一致的,返回结果不为null,List中每个标位也不为null,映射成功的属性有值,没有映射成功的使用默认值
*resultMap:
1.不同名的结果映射
- 高级查询的结果映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxx.mapper.UserMapper">
<insert id="insertUser" parameterType="user">
insert into tb_user(name,sex,age) values(#{name},#{sex},#{age})
</insert>
<!--
keyColumn:指定当前isnert语句中的自增主键的列的列名
keyProperty:指定输入参数中用于存储自增主键值的属性的属性名
-->
<insert id="insertMain" parameterType="orderMain" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
insert into tb_order_main(order_no,user_id,flag,total)
values(#{orderNo},#{userId},#{flag},#{total})
</insert>
<insert id="insertDetail">
insert into tb_order_detail(main_id,goods_id,num,total)
values(#{mainId},#{goodsId},#{num},#{total})
</insert>
<select id="selectMaxId" resultType="int">
select max(id) from tb_order_main
</select>
<delete id="deleteById" parameterType="int">
delete from tb_user where id=#{id}
</delete>
<select id="selectUsers" resultType="user">
select id id2,name name2,sex sex2 from tb_user
</select>
<!--
resultMap:resultMap标签的id
-->
<select id="selectMains" resultMap="map1">
select id,order_no ,user_id,flag,total from tb_order_main
</select>
<!--
定义映射规则
id:唯一标识
type:需要映射的类型的全限定类名或别名
-->
<resultMap type="ordermain" id="map1">
<!--
定义主键列的映射规则
column:指定需要映射的列名
property:指定需要映射到type中的属性的属性名
jdbcType:列在数据库中的类型(省略)
javaType:属性的类型(省略)
-->
<id column="id" property="id"/>
<!--
普通列的映射规则
column:指定需要映射的列名
property:指定需要映射到type中的属性的属性名
jdbcType:列在数据库中的类型(省略)
javaType:属性的类型(省略)
-->
<result column="order_no" property="orderNo"/>
<result column="user_id" property="userId"/>
<result column="flag" property="flag"/>
<result column="total" property="total"/>
</resultMap>
</mapper>