Mybatis简介
Mybatis历史
MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁
移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码于
2013年11月迁移到Github。iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
Mybatis特性
- MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架
- MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java
Objects,普通的Java对象)映射成数据库中的记录 - MyBatis是一个半自动的ORM(Object Relation Mapping)框架
与其他持久化层技术对比:
- JDBC
- SQL夹杂在Java代码中耦合度高,导致硬编码内伤
- 维护不易且实际开发需求中SQL有变化,频繁修改的情况多见
- 代码冗长,开发效率低
- Hibernate和JPA
- 操作简便,开发效率高
- 程序中的长难复杂SQL需要绕过框架
- 内部自动生产的SQL,不容易做特殊优化
- 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难
- 反射操作太多,导致数据库性能下降
Mybatis配置详解
核心配置文件中的标签必须按照固定的顺序:<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//MyBatis.org//DTD Config 3.0//EN"
"http://MyBatis.org/dtd/MyBatis-3-config.dtd">
<configuration>
<!--引入properties文件,此时就可以${属性名}的方式访问属性值-->
<properties resource="jdbc.properties"></properties>
<settings>
<!--将表中字段的下划线自动转换为驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
<typeAliases>
<!--
typeAlias:设置某个具体的类型的别名
属性:
type:需要设置别名的类型的全类名
alias:设置此类型的别名,若不设置此属性,该类型拥有默认的别名,即类名且不区分大小
写
若设置此属性,此时该类型的别名只能使用alias所设置的值
-->
<!--<typeAlias type="com.atguigu.mybatis.bean.User"></typeAlias>-->
<!--<typeAlias type="com.atguigu.mybatis.bean.User" alias="abc">
</typeAlias>-->
<!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写-->
<package name="com.atguigu.mybatis.bean"/>
</typeAliases>
<!--
environments:设置多个连接数据库的环境
属性:
default:设置默认使用的环境的id
-->
<environments default="mysql_test">
<!--
environment:设置具体的连接数据库的环境信息
属性:
id:设置环境的唯一标识,可通过environments标签中的default设置某一个环境的id,
表示默认使用的环境
-->
<environment id="mysql_test">
<!--
transactionManager:设置事务管理方式
属性:
更多Java –大数据 – 前端 – UI/UE - Android - 人工智能资料下载,可访问百度:尚硅谷官网(www.atguigu.com)
四、MyBatis的增删改查
1、添加
2、删除
3、修改
type:设置事务管理方式,type="JDBC|MANAGED"
type="JDBC":设置当前环境的事务管理都必须手动处理
type="MANAGED":设置事务被管理,例如spring中的AOP
-->
<transactionManager type="JDBC"/>
<!--
dataSource:设置数据源
属性:
type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
type="POOLED":使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从
缓存中直接获取,不需要重新创建
type="UNPOOLED":不使用数据库连接池,即每次使用连接都需要重新创建
type="JNDI":调用上下文中的数据源
-->
<dataSource type="POOLED">
<!--设置驱动类的全类名-->
<property name="driver" value="${jdbc.driver}"/>
<!--设置连接数据库的连接地址-->
<property name="url" value="${jdbc.url}"/>
<!--设置连接数据库的用户名-->
<property name="username" value="${jdbc.username}"/>
<!--设置连接数据库的密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="UserMapper.xml"/>
<!--
以包为单位,将包下所有的映射文件引入核心配置文件
注意:此方式必须保证mapper接口和mapper映射文件必须在相同的包下
-->
<package name="com.atguigu.mybatis.mapper"/>
</mappers>
</configuration>
properties
这些属性可以在外部进行配置,并可以进行动态替换。
既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置
引入properties文件,此时就可以${属性名}的方式访问属性值
```xmldriver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
<a name="J5gHY"></a>
### settings
setting配置是Mybatis重要的调整设置,会改变Mybatis运行时的行为,各设置项和默认设置值如下:
| **设置名** | **描述** | **有效值** | **默认值** |
| --- | --- | --- | --- |
| cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
| lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 <br />特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
| aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 <br />否则,每个延迟加载属性会按需加载(参考 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 | 指定发现自动映射目标未知列(或未知属性类型)的行为。<br />- NONE: 不做任何反应<br />- WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)<br />- FAILING: 映射失败 (抛出 SqlSessionException)<br /> | 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)。<br />如果允许使用则设置为 false。 | true | false | True |
| mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,<br />即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
| localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION | STATEMENT | SESSION |
| jdbcTypeForNull | 当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。<br />某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,<br />比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 | OTHER |
| lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,<br />clone,<br />hashCode,<br />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(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
| proxyFactory | 指定 Mybatis 创建可延迟加载对象所用到的代理工具。 | CGLIB | 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 | Specifies 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 qualified class name | Not set |
| nullableOnForEach | Specifies the default value of 'nullable' attribute on 'foreach' tag. (Since 3.5.9) | true | false | false |
<a name="wAswm"></a>
### typeAliases
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写<br />也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,
```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>
<!--别名为类名首字母小写-->
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
下面是一些为常见的 Java 类型内建的类型别名
别名 | 映射的类型 | 别名 | 映射的类型 | |
---|---|---|---|---|
_byte | byte | double | Double | |
_long | long | float | Float | |
_short | short | boolean | Boolean | |
_int | int | date | Date | |
_integer | int | decimal | BigDecimal | |
_double | double | bigdecimal | BigDecimal | |
_float | float | object | Object | |
_boolean | boolean | map | Map | |
string | String | hashmap | HashMap | |
byte | Byte | list | List | |
long | Long | arraylist | ArrayList | |
short | Short | collection | Collection | |
int | Integer | iterator | Iterator | |
integer | Integer |
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 或 SMALLINT |
IntegerTypeHandler | java.lang.Integer, int | 数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler | java.lang.Long, long | 数据库兼容的 NUMERIC 或 BIGINT |
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 类型,用来存储枚举的序数值(而不是名称)。 |
SqlxmlTypeHandler | java.lang.String | SQLXML |
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 或 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> {
// 可以通过两种方式来指定关联的 JDBC 类型:
// 在类型处理器的配置元素上增加一个 jdbcType 属性(比如:jdbcType="VARCHAR");
// 在类型处理器的类上增加一个 @MappedJdbcTypes 注解指定与其关联的 JDBC 类型列表。
// 如果在 jdbcType 属性中也同时指定,则注解上的配置将被忽略。
@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);
}
}
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
objectFactory
每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。
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);
}}
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
plugins
environments
设置具体的连接数据库的环境信息。MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 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”)
- transactionManager 事务管理器的配置(比如:type=”JDBC”)
- dataSource数据源的配置(比如:type=”POOLED”)
databaseIdProvider
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvidermappers
既然 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传统DAO模式
直接使用SqlSession的api进行增删改差:
- 查询api
- 返回单个对象 selectOne
- 返回对象List集合 selectList
- 返回对象Map集合 selectMap
- 增、删、改
- 增加insert()
- 修改update()
删除delete()
{ SqlSessionFactoryBuilder sqlSessionFactoryBuluider =new SqlSessionFactoryBuilder(); InputStream resourceAsStream = null; try { // mybatis-config.xml 是mybatis核心配置为文件 resourceAsStream = Resources.getResourceAsStream("mybaits-config.xml"); } catch (IOException e) { e.printStackTrace(); } SqlSessionFactory factory=sqlSessionFactoryBuluider.build(resourceAsStream) ; sqlSession=factory.openSession(); Emp emp = sqlSession.selectOne("findOne"); List<Emp> emps = sqlSession.selectList("EmpMapper.findAll"); Map<Integer, Emp> empMap = sqlSession.selectMap("findEmpMap", "EMPNO"); sqlSession.insert("addEmp", emp); sqlSession.commit(); sqlSession.update("updateEmp", emp); sqlSession.commit(); sqlSession.delete("deleteEmp", 7936); sqlSession.commit(); }
SqlSession三种传参方式:单个基础数据类型作为参数、多个基础数据类型的Map集合作为参数、引用类型作为参数。
<?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.msb.mapper.EmpMapper"> <!-- 参数为一个基本数据类型 根据员工工号查询员工的全部信息,返回单个员工对象 public Emp findByEmpno(int empno); parameterType 在有参数情况下也是可以省略不写 mybatis 可以根据实际情况自动判断 如果要写parameterType 那么就要写对 在SQL语句上可以使用${} #{} 代表参数的占位 如果参数是单个基本数据类型,{}中名字可以随便写,见名知意 ${} 代表mybatis底层使用Statment语句对象,参数是以字符串拼接的形式设置 #{} 代表mybatis底层使用的preparedStatment语句对象,参数使用?作为占位符处理 #{} 以后常用 --> <select id="findByEmpno" resultType="emp" parameterType="int"> select * from emp where empno = #{empno} </select> <!-- 参数为map集合 查询指定部门号和指定最低薪资的员工信息 20 号部门 且工资在1500以上的员工信息 public List<Emp> findEmpByDeptnoAndSal(int deptno,double sal); < lt; > gt; 最好要进行转译处理,参照HTML转译 w3school在线文档中有转译符号对应规则 Map<String,Object> args=new HashMap<>(); args.put("deptno", 20); args.put("sal", 1500.0); #{}中写的是map集合中,参数的键 --> <select id="findEmpByDeptnoAndSal" resultType="emp" parameterType="map"> select * from emp where deptno = #{deptno} and sal >= #{sal} </select> <!-- 参数为对象 emp >>> deptno sal 参数是我们自定义的类型,那么 #{}中写的是参数的属性名 --> <select id="findEmpByDeptnoAndSal2" resultType="emp" parameterType="emp"> select * from emp where deptno = #{deptno} and sal >= #{sal} </select> </mapper>
Mybatis代理模式(mapper)
几点注意事项
- 使用Mybatis代理模式,接口名字需要和Mapper映射文件名字保持一致(不含拓展名)
- mapper映射文件命名空间namespace必须是是接口全路径名
- sql语句的id必须是接口对应方法名
-
几种传参方式
单个基本数据类型
- 多个基本数据类型
- 单个引用数据类型
- map集合数据类型
多个引用数据类型 ```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">
<!— 多个基本数据类型作为方法参数 ListfindByDeptnoAndSal(@Param(“detpno”) int deptno,@Param(“sal”) double sal); 方式1 arg arg0 arg1 arg2 数字是索引,从0开始 方式2 param param1 param2 param3 数字是编号,从1开始 使用别名 List findByDeptnoAndSal(@Param(“detpno”) int deptno,@Param(“sal”) double sal); 通过@Param注解使用别名之后,就不能再使用arg 但是可以继续使用param —>
<!--单个引用类型,{}中写的使用对象的属性名-->
<select id="findByDeptnoAndSal3" resultType="emp" parameterType="emp" >
select * from emp where deptno =#{deptno} and sal >= #{sal}
</select>
<!--
多个引用类型作为方法参数
List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
如果用@Param定义了别名,那么就不能使用arg*.属性名,但是可以使用param*.属性名和别名.属性名
-->
<select id="findByDeptnoAndSal4" resultType="emp" >
<!-- select * from emp where deptno =#{arg0.deptno} and sal >= #{arg1.sal} -->
select * from emp where deptno =#{param1.deptno} and sal >= #{param2.sal}
<!-- select * from emp where deptno =#{empa.deptno} and sal >= #{empb.sal}-->
</select>
<a name="i7alW"></a>
### 模糊查询
在映射文件中可以使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换。
```xml
<select id="findByEname" resultType="emp" >
select * from emp where ename like concat('%',#{name},'%')
</select>
<select id="testMohu" resultType="User">
select * from emp where ename like "%"#{name}"%"
</select>
主键自增回填.
方式1
useGeneratedKeys:表示要使用自增的主键
keyProperty:表示把自增的主键赋给JavaBean的哪个成员变量。
以添加Dept对象为例,添加前Dept对象的deptno是空的,添加完毕后可以通过getDeptno() 获取自增的主键。
方式2
order:取值AFTER|BEFORE,表示在新增之后|之前执行
keyProperty:执行select @@identity后结果填充到哪个属性中
resultType:结果类型。
<mapper namespace="com.msb.mapper.DeptMapper">
<!--方式1-->
<insert id="addDept" parameterType="dept" useGeneratedKeys="true" keyProperty="deptno">
insert into dept values(null,#{dname},#{loc})
</insert>
<!--方式2-->
<insert id="addDept2" parameterType="dept">
<selectKey order="AFTER" keyProperty="deptno" resultType="int">
select @@identity
</selectKey>
insert into dept values(null,#{dname},#{loc})
</insert>
</mapper>
在很多应用场景中需要新增数据后获取到新增数据的主键值,针对这样的需求一般由三种解决方式:
- 主键自定义,用户通过UUID或时间戳等方式生成唯一主键,当做主键值。在分布式场景中应用较多。
- 查询后通过select max(主键) from 表获取主键最大值。这种方式在多线程访问情况下可能出现问题。
- 查询后通过select @@identity获取最新生成主键。要求这条SQL必须在insert操作之后,且数据库连接没有关闭。
动态SQL
if标签
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中
的内容不会执行 ```xml <?xml version=”1.0” encoding=”UTF-8” ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<a name="ydVTr"></a>
### where标签
where和if一般结合使用:
- 若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
- 若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的<br />and去掉
注意:where标签不能去掉条件最后多余的and
```xml
<select id="findEmpByCondition" resultType="emp">
select * from emp
<where>
<if test="empno != null">
and empno= #{empno}
</if>
<if test="ename != null and ename != ''">
and ename= #{ename}
</if>
</where>
</select>
choose标签
choose、when、otherwise相当于if…else if..else
<select id="findEmpByCondition2" resultType="emp">
select * from emp
<where>
<choose>
<when test="empno != null">
and empno= #{empno}
</when>
<when test="ename != null and ename != ''">
and ename= #{ename}
</when>
</choose>
</where>
</select>
set标签
set和if一般结合使用:
- 若set标签中的if条件都不满足,则set标签没有任何功能,即不会添加set关键字
- 若set标签中的if条件满足,则set标签会自动添加set关键字,并将条件最前方多余的 , 去掉
注意:where标签不能去掉条件最后多余的and
<!--int updateEmpByCondtion(Emp emp);-->
<update id="updateEmpByCondtion" >
update emp
<set>
<if test="ename != null and ename != '' ">
, ename =#{ename}
</if>
<if test="job != null and ename != '' ">
, job =#{job}
</if>
</set>
where empno =#{empno}
</update>
trim标签
trim用于去掉或添加标签中的内容
trim标签常用属性:
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容
bind标签
一般用于处理模糊查询的模板
<select id="findEmp" resultType="emp">
<bind name ="likePatten" value="'%'+param1+'%'" />
select * from emp where ename like #{likepattrn}
</select>
foreach标签
foreach标签属性:
- collection:设置要循环的数组或集合:
- 参数是数组,collection中名字指定为array;
- 参数是List集合,collection中名字指定为list
- item:表示集合或数组中的每一个数据
- separator:设置循环体之间的分隔符
- open:设置foreach标签中的内容的开始符
- close:设置foreach标签中的内容的结束 ```xml
<a name="UVei1"></a>
### sql标签
sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入
```xml
<sql id="empColumn">empno,ename,job,mgr,hiredate,sal,comm,deptno</sql>
<select id="findByCondition" resultType="emp">
select <include refid="empColumn" /> from emp
<trim prefix="where" prefixOverrides="and">
<if test="empno != null">
and empno =#{empno}
</if>
<if test="ename != null and ename != ''">
<bind name="likePattern" value="'%'+ename+'%'"/>
and ename like #{likePattern}
</if>
</trim>
</select>
自定义结果映射ResultMap
resultMap标签属性:
- id:表示自定义映射的唯一标识
- type:查询的数据要映射的实体类的类型
子标签:
- id:设置主键的映射关系
- result:设置普通字段的映射关系
- association:设置多对一的映射关系
- collection:设置一对多的映射关系
属性:
- property:设置映射关系中实体类中的属性名
- column:设置映射关系中表中的字段名
<mapper namespace="com.msb.mapper.EmpMapper"> <!--手动处理数据库查询字段和封装实体类属性之间的映射关系 1 主键一般使用id属性 2 当属性名和查询出的数据表字段名相同 可以不写映射关系 --> <resultMap id="empMap" type="emp"> <id property="empno" column="empno"></id> <result property="name" column="ename"></result> <result property="job" column="job"></result> <result property="sal" column="sal"></result> <result property="hiredate" column="hiredate"></result> <result property="mgr" column="mgr"></result> <result property="comm" column="comm"></result> <result property="deptno" column="deptno"></result> </resultMap> <select id="findByEmpno" resultMap="empMap" > select * from emp where empno =#{empno} </select> </mapper>
一对一关系
一对一关系查询,ReselutMap使用association标签,property为属性名,javaType为用哪个类的对象给属性赋值<?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.msb.mapper.EmpMapper"> <!--Emp findEmpJoinDeptByEmpno(int empno);--> <resultMap id="empJoinDept" type="emp"> <!--设置emp本身的八个属性的映射关系--> <id property="empno" column="empno"></id> <result property="ename" column="ename"></result> <result property="job" column="job"></result> <result property="sal" column="sal"></result> <result property="hiredate" column="hiredate"></result> <result property="mgr" column="mgr"></result> <result property="comm" column="comm"></result> <result property="deptno" column="deptno"></result> <!-- association 处理一对一 封装一对一信息关系的标签 property emp类的属性名 javaType 用哪个类的对象给属性赋值 --> <association property="dept" javaType="dept"> <id column="deptno" property="deptno"></id> <result column="dname" property="dname"></result> <result column="loc" property="loc"></result> </association> </resultMap> <select id="findEmpJoinDeptByEmpno" resultMap="empJoinDept" > select * from emp e left join dept d on e.deptno =d.deptno where empno = #{empno} </select> </mapper>
一对多关系
一对多关系,使用conllection标签,property为属性名,ofType为用那个类本来填充属性<?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.msb.mapper.DeptMapper"> <!--Dept findDeptJoinEmpsByDeptno(int deptno);--> <resultMap id="deptJoinEmps" type="dept"> <id column="deptno" property="deptno"></id> <result column="dname" property="dname"></result> <result column="loc" property="loc"></result> <!--处理一对多关系的标签--> <collection property="empList" ofType="emp" > <!--设置emp本身的八个属性的映射关系--> <id property="empno" column="empno"></id> <result property="ename" column="ename"></result> <result property="job" column="job"></result> <result property="sal" column="sal"></result> <result property="hiredate" column="hiredate"></result> <result property="mgr" column="mgr"></result> <result property="comm" column="comm"></result> <result property="deptno" column="deptno"></result> </collection> </resultMap> <select id="findDeptJoinEmpsByDeptno" resultMap="deptJoinEmps"> select * from dept d left join emp e on d.deptno =e.deptno where d.deptno =#{deptno} </select> </mapper>
多对多关系
多对多关系,拆分成1对1和1对多,结合使用association,collection标签<?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.msb.mapper.ProjectMapper"> <!--Project findProjectJoinEmpsByPid(int pid);--> <resultMap id="projectJoinEmps" type="project"> <id column="pid" property="pid"></id> <result column="pname" property="pname"></result> <result column="money" property="money"></result> <!--一对多 集合属性 collection--> <collection property="projectRecords" ofType="projectRecord"> <id column="empno" property="empno"></id> <id column="pid" property="pid"></id> <!--一对一 --> <association property="emp" javaType="emp"> <id property="empno" column="empno"></id> <result property="ename" column="ename"></result> <result property="job" column="job"></result> <result property="sal" column="sal"></result> <result property="hiredate" column="hiredate"></result> <result property="mgr" column="mgr"></result> <result property="comm" column="comm"></result> <result property="deptno" column="deptno"></result> </association> </collection> </resultMap> <select id="findProjectJoinEmpsByPid" resultMap="projectJoinEmps"> select * from project p left join projectrecord pr on p.pid = pr.pid left join emp e on e.empno = pr.empno where p.pid= #{pid} </select> </mapper>
级联查询
就是利于数据库表间的外键关联关系进行自动的级联查询操作。
使用MyBatis实现级联查询,除了实体类增加关联属性外,还需要在映射文件中进行配置。<?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.msb.mapper.DeptMapper"> <!--Dept findDeptByDeptno(int deptno); select="com.msb.mapper.EmpMapper.findEmpsByDeptno" 调用的另一个SQL语句 javaType="list" 实体类的属性数据类型 column="deptno" 给另一个SQL语句传入的参数列 jdbcType="INTEGER" 参数对应JDBC的数据类型 fetchType="eager" 加载方式 eager 积极加载 lazy延迟加载--> <resultMap id="deptJoinEmps" type="dept"> <id property="deptno" column="deptno"></id> <result property="dname" column="dname"></result> <result property="loc" column="loc"></result> <collection property="empList" select="com.msb.mapper.EmpMapper.findEmpsByDeptno" javaType="list" column="deptno" jdbcType="INTEGER" fetchType="eager" > </collection> </resultMap> <select id="findDeptByDeptno" resultMap="deptJoinEmps"> select * from dept where deptno =#{deptno} </select> </mapper>
级联查询可以设置延迟加载,采用延迟加载步骤:<?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.msb.mapper.EmpMapper"> <select id="findEmpsByDeptno" resultType="emp"> select * from emp where deptno =#{deptno} </select> </mapper>
第一步:全局开关:在sqlMapConfig.xml中打开延迟加载的开关。配置完成后所有的association和collection元素都生效
lazyLoadingEnabled:是否开启延迟加载。是Mybatis是否启用懒加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
aggressiveLazyLoading:当开启时,任何方法的调用都会懒加载对象的所有属性。否则,每个属性会按需加载,
第二步:分开关:指定的association和collection元素中配置fetchType属性。eager:表示立刻加载;lazy:表示延迟加载。将覆盖全局延迟设置。Mybatis缓存
MyBatis的一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。一级存储默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。缓存中的数据使用键值对形式存储数据namespace+sqlid+args+offset >>> hash值作为键,查询出的结果作为值使一级缓存失效的四种情况
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
-
MyBatis的二级缓存
级缓存是SqlSessionFactory级别,是以namespace为标记的缓存,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。默认并不开启。二级缓存开启的条件: 在核心配置文件中,设置全局配置属性cacheEnabled=”true”,默认为true。
<settings> <setting name="cacheEnabled" value="true"/> </settings>
在映射文件中设置标签
<mapper namespace="com.msb.mapper.EmpMapper"> <cache/> <select id="findByEmpno" resultType="emp" useCache="true" flushCache="false"> select * from emp where empno =#{empno} </select> </mapper>
二级缓存必须在SqlSession关闭或提交之后有效
-
使二级缓存失效的情况:
二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性:
eviction属性:缓存回收策略
- LRU(Least Recently Used):最近最少使用的,移除最长时间不被使用的对象。
- FIFO(First in First out):先进先出,按对象进入缓存的顺序来移除它们。
- SOFT :软引用,移除基于垃圾回收器状态和软引用规则的对象。
- WEAK :弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是LRU。
- flushInterval属性:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
- size属性:引用数目,正整数代表缓存最多可以存储多少个对象,太大容易导致内存溢出
- readOnly属性:只读,true/false
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
- false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
MyBatis缓存查询的顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
整合第三方缓存EHCache
添加依赖
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.2</version> </dependency>
创建EHCache的配置文件ehcache.xml ```xml <?xml version=”1.0” encoding=”UTF-8”?> <ehcache xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance“
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true">
<defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU">
3. 设置二级缓存的类型
去各自的sql映射文件里,开启二级缓存,并把缓存类型指定为EhcacheCache
```xml
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
Mybatis逆向工程
<dependencies>
<!-- mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 日志包,方便查看执行信息-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<!-- 代码生成工具jar -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<?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>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
password="123">
</jdbcConnection> -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"
userId="root"
password="root">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置 -->
<javaModelGenerator targetPackage="com.msb.pojo"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.msb.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.msb.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="dept" domainObjectName="Dept"
enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false" >
<columnOverride column="id" javaType="Integer" />
</table>
</context>
</generatorConfiguration>
public class GeneratorSqlmap {
public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("D:\\ideaProjects\\reverse\\target\\classes\\generatorConfig.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);
}
public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
补充一点:
<build>
<!--告诉maven将项目源码中的xml文件也进行编译,并放到编译目录中-->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>