MyBatis
mybatis 框架是现在比较流行的一个持久层框架,以下是我自己写的一些总结,主要讲的时它的使用方式,详细资料请访问他们的官网。
概述
mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement
等繁杂的过程。
mybatis 通过 xml 或注解的方式将要执行的各种 statement
配置起来,并通过 java 对象和 statement
中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
ORM:
Object Relational Mappging 对象关系映射
简单的说:就是把数据库表和实体类及实体类的属性对应起来让我们可以操作实体类就实现操作数据库表。
开发方式
它的使用方法分为两种一种是 xml 配置文件,另一种是直接使用注解。
这两种方式各有优势,其中 xml 配置文件的优势在于思路清晰,维护方便,在需要修改项目时,不需要重新编译,只需要修改配置文件即可;而注解开发,因为不需要去配置文件,所以它的开发速度更快,也是因为这点,近几年来也更流行使用注解开发。
主配置文件的编写,通常命名为SqlMapConfig.xml
首先是主配置文件编写,以及需要注意的地方。在配置完之后,就可以选择两种不同开发方式了。
<?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="jdbcConfig.properties"></properties>
<!--使用typeAliases配置别名,它只能配置domain中类的别名,批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以) -->
<typeAliases>
<package name="com.itheima.domain"></package>
</typeAliases>
<!-- 配置 mybatis 的环境 -->
<environments default="mysql">
<!-- 配置 mysql 的环境 -->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置连接数据库的信息:用的是数据源(连接池) -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<!-- 告知 mybatis 映射配置的位置 -->
<mappers>
<package name="com.itheima.dao"></package>
</mappers>
</configuration>
- 在使用
properties
标签配置时,我们可以配置文件来指定属性配置。这样,下面的数据库信息就可以引用配置文件,方便以后修改。 - 在使用
typeAliases
标签,我们可以也可以采用自定义别名的方式来开发,别名不区分大小写。 mappers
标签有两种子标签mapper
标签里面有两个常用属性(注意:资源路径之间用/分隔,包路径用.分隔。)mapper
标签里的的resource
属性使用相对于类路径的资源,使用 xml 开发时才使用。mapper
标签里的的class
属性使用mapper
接口类路径,使用注解开发时才使用。(注意:此种方法要求mapper
接口名称和mapper
映射文件名称相同,且放在同一个目录中。)
package
标签里的name
属性指定包下的所有mapper
接口,两种开发方式均可使用,也更为常用,它可以用来替代mapper
标签。(注意:此种方法要求mapper
接口名称和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.itheima.dao.IUserDao">
<!-- 配置 查询结果的列名和实体类的属性名的对应关系 -->
<resultMap id="userMap" type="uSeR">
<!-- 主键字段的对应 -->
<id property="userId" column="id"></id>
<!--非主键字段的对应-->
<result property="userName" column="username"></result>
<result property="userAddress" column="address"></result>
<result property="userSex" column="sex"></result>
<result property="userBirthday" column="birthday"></result>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userMap">
<!--select id as userId,username as userName,address as userAddress,sex as userSex,birthday as userBirthday from user;-->
select * from user;
</select>
</mapper>
- 在使用
mapper
标签时,其中的namespace
属性必须是 dao 类的全限定类名,因为 mabatis 框架是根据namespace
属性和statemen
标签中的id
属性来对数据库进行操作的,也就是根据com.itheima.dao.IUserDao.findAll 反射来确定的。 - 数据库中表的列名与我们实体类中的成员变量名不一致时,我们可以通过取别名的方式来对结果封装,或者是用
resultMap
标签来指定列名及其对应的变量名,当我们在对查询结果封装需要用到它时,就可以直接使用resultMap
属性来引用它,如果不需要时,就可以直接使用resultType
属性来直接指定所要封装的类。 - 对数据库的四种增删改查操作分别对应
insert
delete
update
select
四种标签
根据条件查询时
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultMap="userMap">
select * from user where id = #{uid}
</select>
<!-- 根据名称模糊查询 -->
<select id="findByName" parameterType="string" resultMap="userMap">
select * from user where username like #{name}
<!-- select * from user where username like '%${value}%'-->
</select>
- 查询的条件使用
#{xxx}
来作为占位符使用,当只有一个条件时,花括号里的内容可以随便写,当有多个条件时,花括号里的内容就必须与实体类中的变量名相同。 - 使用模糊查询时的占位符有两种形式
#{name}
或'%${value}%'
,前者花括号内可以随便写,而后者必须写value
,但是前者的 % 符号只能在 java 语句中去拼接,而后者就可以在配置文件中写好。
动态sql语句
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from user
<where>
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>
where
标签是用来代替 sql 语句中的where
或者and
关键字的,它会根据实际情况判断。if
标签是用来判断是否需要用到这个条件时的。foreach
标签就是进行范围查询时使用,其中的collection
属性对应实体类中的变量名,open
和close
属性就是单纯的拼接,item
属性是指定每一个遍历的变量名,separator
属性是指定分隔符。标签体中的内容与就是每一次遍历的占位符,花括号里的内容与item
属性保持一致。
多表查询
在 mybatis 中只有一对一和一对多,它将多对多当成了多个一对多,而多对一就是多个一对一。
一对一
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容-->
<association property="user" column="uid" javaType="user">
<id property="id" column="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
</select>
- 使用
association
标签来指定关联的对应表的单条数据,其中的property
属性对应实体类中的变量名,column
属性对应关联的外键名称,javaType
指定要封装的实体类。在标签体中,必须先写从表的主键列。
一对多
<!-- 定义User的resultMap-->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!-- 配置user对象中accounts集合的映射 -->
<collection property="accounts" ofType="account">
<id column="aid" property="id"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select * from user u left outer join account a on u.id = a.uid
</select>
- 使用
collection
标签来指定关联的对应表的多条数据,其中其中的property
属性对应实体类中的变量名,ofType
指定每一条数据所要封装的实体类。在标签体中,必须先写从表的主键列。
延迟加载和立即加载
延迟加载就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。在一对多时通常采用延迟加载,而在一对一时通常采用立即加载,因为这样能提高数据库性能。
首先要在主配置文件中开启延迟加载
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
然后在映射文件中进行配置
<!-- 定义封装account和user的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
<!-- 根据用户id查询账户列表 -->
<select id="findAccountByUid" resultType="account">
select * from account where uid = #{uid}
</select>
<!-- 定义User的resultMap-->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<!-- 配置user对象中accounts集合的映射 -->
<collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid" column="id"></collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select * from user
</select>
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user">
select * from user where id = #{uid}
</select>
- 上面分别是一对一和一对多的延迟加载配置,其实就是在
association
标签或者是collection
标签中添加select
属性来指定关联的表需要用到哪个方法来查询,即对应的类的dao接口的全限定名.方法名,以及column
属性来指定关联的外键名称。可以省去我们在多表查询时编写复杂的 sql 语句。
注解开发方式
在使用注解开发时,不要有映射配置文件的存在,否则程序会以为你要使用 xml 配置文件开发。
基本使用方法
public interface IUserDao {
@Select("select * from user")
@Results(id="userMap",value={
@Result(id=true,column = "id",property = "userId"),
@Result(column = "username",property = "userName"),
@Result(column = "address",property = "userAddress"),
@Result(column = "sex",property = "userSex"),
@Result(column = "birthday",property = "userBirthday")
})
List<User> findAll();
@Select("select * from user where id=#{id} ")
@ResultMap("userMap")
User findById(Integer userId);
}
- 只需要在方法上添加注解,并且在注解中表明 sql 语句即可。对数据库的四种增删改查操作D分别对应
@Insert
@delete
@Update
@Select
四种注解。 - 数据库中表的列名于我们实体类中的成员变量名不一致时,我们可以通过取别名的方式来对结果封装,或者是用
@Results
标签来指定列名及其对应的变量名。当其它的方法在对查询结果封装需要用到它时,就可以直接使用@ResultMap
注解来引用它。
根据条件查询时
@Select("select * from user where id=#{id} ")
@ResultMap("userMap")
User findById(Integer userId);
@Select("select * from user where username like #{username} ")
@ResultMap("userMap")
List<User> findUserByName(String username);
- 查询的条件使用
#{xxx}
来作为占位符使用,当只有一个条件时,花括号里的内容可以随便写,当有多个条件时,花括号里的内容就必须与实体类中的变量名相同。 - 使用模糊查询时的占位符只有一种形式
#{name}
,花括号内可以随便写。
动态 sql 语句查询
使用注解进行动态 sql 语句查询时,需要用到上述 xml 开发方式里的动态 sql 语句查询的标签,并且将其置于<script>
</script>
标签中,将其当作脚本执行,使用起来极其不便,不推荐使用。
多表查询
@Select("select * from account")
@Results(id="accountMap",value = {
@Result(id=true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(property = "user",column = "uid",
one=@Oneselect(select="com.itheima.dao.IUserDao.findById",
fetchType= FetchType.EAGER))
})
List<Account> findAll();
-
在一对一查询时,使用
@Result
注解来指定关联的对应表的单条数据,其中的property
属性对应实体类中的变量名,column
属性对应关联的外键名称,然后在注解中使用@One
注解添加select
属性来指定关联的表需要用到哪个方法来查询,即对应的类的dao接口的全限定名.方法名,以及fetchType
属性来指定加载方式。@Select("select * from user") @Results(id="userMap",value={ @Result(id=true,column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "address",property = "userAddress"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday"), @Result(property = "accounts",column = "id", many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY)) }) List<User> findAll();
-
在一对多查询时,使用
@Result
注解来指定关联的对应表的单条数据,其中的property
属性对应实体类中的变量名,column
属性对应关联的外键名称,然后在注解中使用@Many
注解添加select
属性来指定关联的表需要用到哪个方法来查询,即对应的类的dao接口的全限定名.方法名,以及fetchType
属性来指定加载方式。