文章目录
MyBatis快速入门
MyBatis是持久层框架,简化了JDBC开发。JDBC存在硬编码与操作繁琐的弊端,而MyBatis几乎免除所有JDBC设置参数与获取结果集的工作。
使用步骤:
- Maven创建模块,导入坐标。
- 编写mybatis-config.xml核心配置文件。
- 创建XxxMapper.java接口文件。
- 编写XxxMapper接口对应的SQL映射文件XxxMapper.xml(与接口同名)。
- MyBatis配置文件中注册Mapper
- Java程序中定义实体类,加载核心配置文件,获取SqlSessionFactory对象
- 获取SQLSession对象,执行SQL语句
- 处理结果,并释放资源
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> <settings> <!--在控制台显示SQL语句--> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <!--给实体类取别名, 让实体类使用更简单, 别名使用时不区分大小写--> <typeAliases> <!--package标签: 给包中的所有类取别名, 默认别名就是类名 name: 包名--> <package name="com.lxl.javabean"/> <!-- <typeAlias alias="Tag" type="domain.blog.Tag"/> 单个别名--> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--配置连接池需要的参数--> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/db1?useSSL=false&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--配置接口映射文件/SQL映射文件的位置--> <mappers> <!--package标签: 扫描包中的所有接口和映射文件 name: 包名 需要满足两个条件: 1.接口名和接口映射文件名字要一样 2.编译后接口和接口映射文件要在同一个包中--> <package name="com.lxl.mapper"/> <!-- <mapper resource="com.lxl.mapper.UserMapper.xml"/> 单个xml引入 <mapper class="com.lxl.mapper.UserMapper"/> 单个接口引入 --> </mappers> </configuration>
Java程序代码:
String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获取SQLSessionFactory对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //创建一个sqlSession数据库连接会话 //List<User> users = sqlSession.selectList("com.lxl.mapper.UserMapper.selectUsersById",1); //selectList方法的字符串参数对应mapper标签中的namespace与select标签中的id // getMapper(XxxMapper.class): 内部会使用动态代理生成接口的实现类 (代理对象) UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.selectUsersById(1); sqlSession.close();//关闭资源
核心配置文件
配置文件结构:
properties属性
概述:properties作用类似于我们以前使用的properties文件,用来配置系统中固定的参数信息,比如数据库连接地址、用户名、密码等。
properties使用:
<properties resource="mysql.properties"> <property name="username" value="root"/> <property name="password" value="root"/> </properties> <!--在 properties 节点中设置了 username、password 这四个参数值, 然后在 dataSource 节点配置中,用 ${username} 的方式读取到 root 值。--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> <!--若mysql.properties有username、password字段,则property中属性将被覆盖--> </dataSource> </environment> </environments>
加载优先级:
- 首先读取在 properties 元素体内指定的属性。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。
SqlSessionFactory factory =
new SqlSessionFactoryBuilder().build(reader,environment, property);
- 因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最次则是 properties 元素中指定的属性。
settings设置
概述:这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
settings使用:
<settings> <!--指定MyBatis所用日志的具体实现,在控制台显示SQL语句--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--允许 JDBC 支持自动生成主键,需要数据库驱动支持。默认false。--> <setting name="useGeneratedKeys" value="ture"/> <!--设置超时时间,它决定数据库驱动等待数据库响应的秒数。默认未设置。--> <setting name="defaultStatementTimeout" value="1000"/> <!--是否开启驼峰命名自动映射,从数据库列名A_COLUMN映射到属性名aColumn。--> <setting name="mapUnderscoreToCamelCase" value="true"/> <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存(二级缓存)--> <setting name="cacheEnabled" value="false"/> <!--指定全局的自动映射等级:Nome禁用,Partial嵌套以外,Full全部包括嵌套--> <setting name="autoMappingBehavior" value="PARTIAL"/> </settings>
typeAliases类型别名
概述:类型别名可为Java实体类型设置一个缩写名字。 它仅用于XML配置,意在降低冗余的全限定类名书写。
typeAliases别名使用:
<typeAliases> <typeAlias alias="Student" type="com.lxl.bean.Student"/> <typeAlias alias="Teacher" type="com.lxl.bean.Teacher"/> </typeAliases> <!--Student可在任何XxxMapper.xml中替换com.lxl.bean.Student--> <!--或者使用包搜索形式,全部指定默认别名,默认为对应类类名--> <typeAliases> <package name="com.lxl.bean"/> </typeAliases> <!-- 在没有注解的情况下,会使用Bean实体类的首字母小写的非限定类名来作为它的别名。 比如com.lxl.bean.Student的别名为teacher; 若有注解,则别名为其注解值: @Alias("author") public class Author { } -->
MyBatis内建别名:
- 基本数据类型别名格式:
byte -> _byte
,类型名前加下划线- 包装类别名格式:
Byte -> byte
,类名全小写- 其他常用类:
Object -> object
,类名全小写
environment环境配置
概述:
MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中。例如开发、测试和生产环境需要有不同的配置;或者想在具有相同Schema约束的多个生产数据库中使用相同的SQL映射。还有许多类似的使用场景。
尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。意味着一个数据库对应一个SqlSessionFactory类实例
SqlSessionFactory实例创建:
SqlSessionFactory factory = //指定环境 new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = //使用默认环境 new SqlSessionFactoryBuilder().build(reader); SqlSessionFactory factory = //指定环境,并配置属性 new SqlSessionFactoryBuilder().build(reader, environment, properties);
配置环境:
<environments default="development"><!--default:默认environment的id--> <environment id="development"> <transactionManager type="JDBC"> <!--事务管理器的配置:--> <!--JDBC:直接使用了JDBC的提交和回滚机制,依赖从数据源获得的连接来管理事务。--> <!--MANAGED:使用容器来接管事务的整个生命周期--> <property name="..." value="..."/> </transactionManager> <dataSource type="POOLED"> <!--数据源的配置(使用的连接池):--> <!--POOLED:采用传统的javax.sql.DataSource规范中的连接池--> <!--UNPOOLED:采用传统的获取连接的方式,实现标准接口,但无连接池概念。--> <!--JNDI:采用服务器提供的JNDI技术实现,如tomcat服务器使用dbcp连接池--> <!--UNPOOLED具体property配置见官网详情页--> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> <environment id="test">...</environment> </environments>
XML映射器
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
- cache – 该命名空间的缓存配置。
- cache-ref – 引用其它命名空间的缓存配置。
- resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
- parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
- sql – 可被其它语句引用的可重用语句块。
- insert – 映射插入语句。
- update – 映射更新语句。
- delete – 映射删除语句。
- select – 映射查询语句。
select标签
select常见属性列表:
属性 描述 id 在命名空间中唯一的标识符,可以被用来引用这条语句。 parameterType 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为MyBatis可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 resultType 期望从这条语句中返回结果的实体类全限定名或别名。 resultMap 对外部resultMap标签的命名引用。 resultSets 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。 timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 SQL语句中的占位符:
#{id}
:先使用?占位符,执行SQL语句时预处理将?转为具体值。${id}
:直接拼接SQL语句字符串,存在SQL注入安全问题。
insert/update/delete标签
三者常用标签:
属性 描述 id 在命名空间中唯一的标识符,可以被用来引用这条语句。 parameterType 参照select标签的parameterType属性描述 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)中,当主键列不是表中的第一列的时候是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 注意:当不支持自动生成主键列的数据库和可能不支持自动生成主键的JDBC驱动,可使用selectkey标签,具体实现查看官方详情页。
sql语句片标签
作用:这个元素可以用来定义可重用的SQL代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的include元素中定义不同的参数值。
sql标签使用:
<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"/> <!--作用在sql标签中,导入的id为sometable的sql标签--> <property name="include_target" value="sometable"/> <!--导入的sql标签中的include标签,去导入id为sometable的sql标签--> </include> <!--最后include标签导入的SQL语句为:from SomeTable--> </select> <!-- XML文件中部分特殊字符不允许作为sql语句,需进行处理 1. 转义字符,如 < 写为 < 2. CDATA区,<![CDATA[ 内容 ]]> -->
resultMap标签
作用:当数据库中表的字段与bean中的实体类属性命名不一致时,可使用结构映射。即resultType属性填写bean中对应实体类后,MyBatis幕后自动生成的ResultMap不能使列名与属性名匹配上时,可以通过自定义resultMap解决。
解决列名与属性名不匹配:
<!--方法一:若列名为a_column,属性名为aColumn的老式命名规则--> <!--可以在mybatis-config.xml文件中setting设置默认生成resultMap,将查询的列名转为驼峰命名的实体类属性名--> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
<!--方法二:查询sql语句中定义列别名,对应实体类的属性名--> <select id="selectUsers" resultType="User"> select user_id as "id", user_name as "userName", from some_table where id = #{id} </select>
<!--方法三:自定义resultMap映射标签,select中显式引入外部resultMap--> <resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <!--主键列的映射标签使用id,该字段被指定为唯一标识,以此提高性能--> <result property="username" column="user_name"/> <!--非主键列映射标签使用result--> </resultMap> <select id="selectUsers" resultMap="userResultMap"> select user_id, user_name from some_table where id = #{id} </select>
resultMap属性:
属性 描述 id 当前命名空间中的一个唯一标识,用于标识一个结果映射。 type 类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。 autoMapping 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。 extends 该resultMap继承另一个resultMap,一般而言对应实例类的继承关系,结果注入时,确保父元素的映射关系无误。即只需写子类的特有属性,该属性自行获取父类的resultMap来映射父类中属性。 resultMap的子标签:
标签名 标签描述 constructor 用于在实例化类时,注入结果到构造方法中。 id 注入到字段或JavaBean属性的主键结果,标识id可提高性能。 result 注入到字段或JavaBean属性的普通结果。 association 一个复杂类型的关联,可将许多结果将包装成指定类型 collection 一个复杂类型的集合,将结果集包装成对应类型后加入集合 discriminator 使用结果值来决定使用那个resultMap
id、result映射标签
作用:这些元素是结果映射的基础。id和result元素都将一个列的值映射到一个简单数据类型(String,int,double,Date等的属性或字段。
属性:
属性 描述 property 映射到列结果的字段或属性。如果JavaBean有这个名字的属性(property),会先使用该属性。否则MyBatis将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 javaType 一个Java类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个JavaBean,MyBatis通常可以推断类型。然而,如果你映射到的是HashMap,那么你应该明确地指定javaType 来保证行为与期望的相一致。
constructor构造器标签:
作用:resultMap标签中的id和result标签映射到javabean类都是通过修改对象属性(set)。但某些情况下,实体类没有定义无参构造与set方法(有参构造的不可变类),此时可通过定义constructor标签将查询结果注入到对应bean类有参构造器中并生成对象。(即使MyBatis支持私有属性映射,但有些需求更青睐通过构造器生成bean类实例)。
子标签:
标签名 标签描述 idArg 被注入到构造方法的一个主键结果,标识id可提高性能。 arg 被注入到构造方法的一个普通结果 子标签属性:
属性 描述 column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。同时可认为给select属性嵌套查询提供参数。 javaType 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 select 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。后续association标签中举例。 resultMap 结果映射的ID,可以将嵌套的结果集映射到一个合适的对象树中。 它可以作为使用额外select语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet。这样的ResultSet将会将包含重复或部分数据重复的结果集。为了将结果集正确地映射到嵌套的对象树中,MyBatis 允许你“串联”结果映射,以便解决嵌套结果集的问题。后续association标签中举例。 name 构造方法形参的名字。从3.4.3版本开始,通过指定具体的参数名,可以以任意顺序写入arg元素。 使用:
<!--构造器:public User(int in,String username,int age)--> <resultMap id="userMap" type="user"> <constructor> <idArg column="id" javaType="int" name="id" /> <arg column="age" javaType="_int" name="age" /> <arg column="username" javaType="String" name="username" /> </constructor> </resultMap>
association关系标签
作用:
- 关联(association)元素处理“一对一”类型的关系,如杯子与杯盖的关系,正常来说一个杯子有一个杯盖,同时杯子与杯盖都是javabean类型。
- 关联的不同之处是,你需要告诉MyBatis如何加载关联。MyBatis有两种不同的方式加载关联:
- 嵌套Select查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
- 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
- 普通的结果映射相比,它只在 select 和 resultMap 属性上有所不同。
基本使用:
<resultMap id="userMap" type="user"> <id property="id" column="id"></id> <association javaType="UserInfo" property="userinfo" > <id property="id" column="id"></id> <result property="isMarry" column="is_marry"/> </association> <!--将查询的结果集中对应列的值注入userinfo实例的属性中--> </resultMap>
属性:
属性 描述 property 映射到列结果的字段或属性。如果用来匹配的JavaBean存在给定名字的属性,那么它将会被使用。否则MyBatis将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 javaType 一个 Java 类的完全限定名,或一个类型别名。 如果你映射到一个JavaBean,MyBatis通常可以推断类型。然而如果你映射到的是HashMap,那么你应该明确地指定javaType 来保证行为与期望的相一致。 column 数据库中的列名,或者是列的别名。注意:在使用复合主键的时候,你可以使用 column=“{prop1=col1,prop2=col2}” 这样的语法来指定多个传递给嵌套Select查询语句的列名。这会使得prop1和prop2作为参数对象,被设置为对应嵌套Select语句的参数。 select 用于加载复杂类型属性的映射语句的ID,它会从column属性指定的列中检索数据,作为参数传递给目标select语句。 fetchType 懒加载(使用该嵌套查询的结果时才加载),有效值为lazy和eager。 指定属性后,将在映射中忽略全局配置参数lazyLoadingEnabled,使用属性的值。 resultMap 结果映射的ID,详情参照constructor标签中的解释。 columnPrefix 指定columnPrefix列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中,如子标签中的column="id"且父标签中的columnPrefix=“stu”,那么实际上读取的列名为stu_id。 autoMapping 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性autoMappingBehavior。注意,本属性对外部的结果映射无效,所以不能搭配select或resultMap元素使用。默认值:未设置(unset)。
嵌套select属性
使用案例:
<resultMap id="selectUserById" type="user"> <association property="userinfo" column="id" javaType="UserInfo" select="selectUserInfoById"/> <!--获取第一次查询到的id,作为嵌套查询的条件参数--> </resultMap> <select id="selectUserInfoById" resultMap="userinfo"> SELECT * FROM userinfo WHERE user.id = #{id}; </select>
嵌套select查询弊端:
这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 概括地讲,N+1 查询问题是这样子的:
- 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
- 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。
嵌套select查询的结果映射:
<resultMap id="selectUserById" type="user"> <id property="id" column="id" ></id> <association property="userinfo" column="id" javaType="UserInfo" select="selectUserInfoById" resultMap="userInfoMap"/> <!--将嵌套查询的结果依照映射userInfoMap,注入UserInfo实例中--> </resultMap> <resultMap id="userInfoMap" type="UserInfo"> <id property="id" column="id"></id> <result property="isMarry" column="is_marry"/> </resultMap>
discriminatior选择标签
作用:当实体类中的某个属性可能对应多种其类型的子类型结果,查询的时候需要甄别是需要用到那种子类型的resultMap进行数据映射。只要查询返回了不同的结果,就用discriminator。
注意:
- 当该鉴别器匹配到一个结果后,其余case就会被忽略。类似于java中的switch-case。
- 若匹配不到任何一个case,mysql只会使用鉴别器外的映射关系。
使用:
<resultMap id="userMap" type="user"> <id property="id" column="id" /> <result property="name" column="username"/> <discriminator javaType="int" column="sex_type"> <!--根据性别类型,进行不同的sex属性映射--> <case value="0" resultMap="girlResultMap"/> <case value="1" resultMap="boyResultMap"/> </discriminator> </resultMap>
cache缓存标签
概述:现在我们每次执行相同的SQL语句都是去数据库中查询,存在效率问题。MyBatis框架提供了缓存策略,通过缓存策略可以减少查询数据库的次数,提升系统性能。在MyBatis框架中缓存分为一级缓存和二级缓存。一般来说缓存在内存中。
一级缓存:
- sqlSession范围的缓存,只能在同一个sqlSession内部有效。它本身已经存在,一级缓存不需要手动处理,可以直接使用。
- 可以通过MyBatis配置文件的setting中设置缓存
- SqlSession的
clearCache
方法可清理一级缓存。- 当调用SqlSession的修改、添加、删除、提交、关闭等方法时,一级缓存会被清空。
二级缓存:
- 二级缓存是mapper映射级别缓存,作用范围跨越SqlSession,即可以在多个SqlSession之间共享二级缓存数据。
开启二级缓存:
<settings> <setting name="cacheEnabled" value="true"/> <!--mybatis-config.xml配置开启二级缓存--> </settings> <mapper namespace="com.lxl.bean.UserMapper"> <cache/> <!--cache标签表示该mapper文件查询结果将会放入二级缓存--> </mapper>
public class User implements Serializable{ ... } //对应查询的实体类实现Serializable接口 //若不在UserMapper.xml文件中添加cache标签 //还可以通过注解的形式开启mapper二级缓存 @CacheNameSpace(blocking = true) public interface UserMapper{ ... } public void test04() { // 第一个sqlSession SqlSession sqlSession1 = MyBatisUtils.getSqlSession(); //工具类MyBatisUtils获取sql会话 UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class); User user1 = mapper1.findUserById(1); System.out.println("第一次" + user1.getUsername()); sqlSession1.close(); // 要关闭会话,数据才会保存二级缓存中 // 第二个sqlSession SqlSession sqlSession2 = MyBatisUtils.getSqlSession(); UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); User user2 = mapper2.findUserById(1); System.out.println("第二次" + user2.getUsername()); sqlSession2.close(); }