IT学习笔记--MyBatis

1.MyBatis介绍

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

与 JDBC 相比, MyBatis 简化 了相关代码, SQL 语句在一行代码中就能执行 。 MyBatis 提供了一个映射引擎 , 声明式地将 SQL 语句的执行结果与对象树映射起来。通过使用 一种内建的类XML 表达式语言, SQL 语句可以被动态生成。

与Hibernate相比,Hibernate屏蔽了SQL,只能全表映射。无法根据不同的条件组装不同的SQL,对多表关联和复杂SQL查询支持较差,需要自己写SQL,返回后,需要自己将数据组装为POJO。且不能有效支持存储过程。

MyBatis 支持声明式数据缓存( declarative data caching ) 。当 一条 SQL 语句被标记为“可缓存”后,首次执行它时从数据库获取的所有数据会被存储在高速缓存中,后面再执行这条语句时就会从高速缓存中读取结果,而不是再次命中数据库 。 MyBatis 提供了默认情况下基于 Java HashMap 的缓存实现,以及用于与 OS Cache 、 Ehcache 、 Hazeleast 和 Memcached 连接的默认连接器,同时还提供了 API 供其他缓存实现使用。

2. 在实体类中不要使用基本类型 。 基本类型包括 byte 、 int , short 、 long 、 float 、double 、 char 、 boolean。

MyBatis 使用 Java 的动态代理可以直接通过接口来调用 相应 的方法,不需要提供接口的实现类,更不需要在实现类中使用 SqlSession以通过命名空 间间接调用 。 另外,当有多个参数的时候,通过参数注解@Param 设置参数的名字省去了手动构造 Map 参数的过程,尤其在 Spring 中使用的时候,可以配置为自动扫描所有的接口类 ,直接将接口注入需要用到的地方。

注意:

  • 当 Mapper 接口和 XML 文件关联的时候,命名空间口amespace 的值就需要配置成接口的全限定名称。
  • XML 中的 select 标签的 id 属性值和定义的接口方法名是一样的 ρMyBatis就是通过这种方式将接口方法和 XML 中定义的SQL 语句关联到一起的,如果接口方法没有和XML 中的 id 属性值相对应,启动程序便会报错。
  • 接口中定义的返回值类型必须和XML 中配置的 resultType 类型一致,否则就会因为类型不一致而抛出异常。返回值类型是由 XML 中的 resultType (或 resultMap 中的 type )决定的,不是由接口中写的返回值类型决定的。
  • 如果使用 result Type 来设置返回结果的类型,需要在 SQL 中为所有列名和属性名不一致的列设置别名,通过设置别名使最终的查询结果列和 resultType 指定对象(即实体类)的属性名保持一致,进而实现自动映射。

 3. MyBatis 提供 了 一个全局属性mapUnderscoreToCamelCase ,通过配置这个属性为 true 可以自动将以下画线方式命名的数据库列映射到 Java 对象的驼峰式命名属性中,这个属性默认为 false。如:

<settings>
〈!--其它配置 --〉
<setting name=” mapUnderscoreToCamelCase” value=”true ” />
</settings>
使用上述配置的时候,前面的 selectAll 可以改写如下 。
<select id= ” selectAll ” resultType=” tk.mybatis.simple.model.SysUser” >
select_id,user_name ,user_password,user_email ,user_info,head_img,create_time
from sys_user
</select>
而无需写成:
<select id=” selectAll ” resultType=”t k.mybatis.simple.model . SysUser” >
select id,
user_name userName,
user_password userPassword ,
user_email userEmail,
user_info userinfo ,
head_img headimg,
create_time createTime
from sys_user
</select>

BLOB 对应的类型是 ByteArrayinputStream ,就是二进制数据流 。由于数据库区分 date 、 time 、 datetime 类型,但是 Java 中一般都使用 j ava.util. Date类型 。 因此为了保证数据类型的正确,需要手动指定日期类型, date 、 time 、 date t ime 对应的 JDBC 类型分别为 DATE 、 TIME 、 TIMESTAMP 。

使用< selectKey>标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库。而useGeneratedKeys这种回写主键的方法只适用于支持主键自增的数据库。

4. 接口参数

对于参数比较少的情况 ,有两种方式可以采用:使用 Map 类型作为参数或使用@ Param 注解。因为第一种方式需要自己手动创建 Map 以及对参数进行赋值,这样并不简洁,所以推荐使用@ Param 注解。

给参数配置@ Param 注解后, MyBatis 就会自动将参数封装成 Map 类型,@ Param 注解值会作为 Map 中的 key,因此在 SQL 部分就可以通过配置的注解值来使用参数。具体使用如下:

List<SysRole> selectRolesByUserAndRole(
@Param (” user ”) SysUser user, 
@Param (” role ”) SysRole role );

当参数类型是一些 JavaBean 的时候,用法略有不同。如下将接口方法中的参数换成 JavaBean 类型:

List<SysRole> selectRolesByUserAndRole(
@Param (” user ”) SysUser user,
@Param (” role ”) SysRole role );

这时,在 XML 中就不能直接使用#{ userid }和#{ enabled }了,而是要通过点取值方式使用#{ user.id }和#{ role . enabled } 从两个 JavaBean 中取出指定属性的值 。

5. Mybatis动态SQL

MyBatis 的动态 SQL 在 XML 中支持的几种标签:

  • if
  • choose ( when、otherwise)
  • trim(where、set)
  • foreach
  • bind

1)if

if标签通常用于 WHERE 语句中,通过判断参数值来决定是否使用某个查询条件,它也经常用于 UPDATE 语句中判断是否更新某一个字段 , 还可以在 INSERT 语句中用来判断是否插入某个字段的值。如:

<select id=”selectByUser” resultType=” tk.mybatis.simple.model.SysUser” >
select id,
user_name userName ,
user_password userPassword ,
user_email userEmail ,
user_info userinfo ,
head_img headimg ,
create_time createTime
from sys_user
where 1 = 1
<if test=” userName != null and userName != ''”>
and user name like concat (’%’,#{userName}, ’%’ )
</if>
<if test=” userEmail != null and userEmail !='' ” >
and user email = #{userEmail}
〈/if>
</select>

注意:if 标签有一个必填的属性 test, test 的属性值是一个符合 OGNL 要求的判断表达式,表达式的结果可以是 true 或 false ,除此之外所有的非0值都为 true ,只有0为 false 。 为了方便理解,在表达式中 ,建议只用 true 或 false 作为结果 。

2)choose

if 标签提供了基本的条件判断,但是它无法实现 if. . . else 、 if ... else ...的逻辑,要想实现这样的逻辑,就需要用到 choose when otherwise 标签。 choose 元素中包含 when和 otherwise 两个标签,一个 choose 中至少有一个 when ,有 0 个或者1个otherwise。如:

<select id=”selectByidOrUserName” resultType=” tk.mybatis.simple.model.SysUser” >
    select id,
    user_name userName , user_password userPassword , user_email userEmail ,
    user_info userinfo , head_img headimg , create_time createTime
    from sys_ user
    where 1 = 1
  <choose>
    <when test=” id != null ” >
        and id= #{id}
    </when >
    <when test=” userName != null and userName !='' ”>
        and user_name = #{userName}
    </when>
    <otherwise>
        and 1 = 2
    </otherwise>
  </choose>
</select>

3)where

where 标签的作用:如果该标签包含的元素中有返回值,就插入一个 where ;如果 where后面的字符串是以 AND 和 OR 开头的,就将它们剔除。如:

<select id=” selectByUser” resultType=” tk.mybatis.simple.model.SysUser” >
select id,
user_name userName,
user_password userPassword ,
user_email userEmail ,
user_info userinfo ,
head_img headimg,
create_time createTime
from sys_user
<where>
<if test=”userName != null and userName !='' ”>
and user_name like concat ( ’%’,#{userName}, ’%’)
</if>
<if test=” userEmail ! = '' and userEmail != null ”>
and user_email = #{userEmail}
</if>
</where>
</select >

4)set

set 标签的作用:如果该标签包含的元素中有返回值,就插入一个 set :如果 set 后面的字符串是 以逗号结尾的,就将这个逗号剔除 。

<update id=”updateByidSelective ” >
update sys_user
<set>
<if test=”userName != null and userName !='' ”>
user_name= #{userName} ,
</if>
<if test=” userPassword != null and userPassword ! = '' ”>
user_password= #{userPassword} ,
</if>
<if test=”userEmail != null and userEmail != '' ”>
user_email = #{userEmail} ,
</if>
<if test=” userinfo != null and userinfo !='' ”>
user_info = #{userinfo},
</if>
<if test=”headimg != null ” >
head_img = #{headimg, jdbcType=BLOB},
</if>
<if test=”createTime != null ”>
create_time = #{createTime, jdbcType=TIMESTAMP},
</if>
id = # {id} ,
</set>
where id = #{id}
</update>

5)trim

where 和 set 标签 的功能都可以用 trim 标签来实现,并且在底层就是通过TrimSqlNode 实现的 。where 标签对应 trim 的实现如下:
<trim prefix=”WHERE ” prefixOverrides=”AND IOR ” >  
</ trim>
set 标签对应 的 trim 实现如下 :
<trim suffix=” SET” suffixOverrides=”, ” >
</ trim

trim 标签有如下属性:
prefix :当 trim 元素内包含内容时,会给内容增加 prefix 指定的前缀。
prefixOverrides :当 trim 元素内包含内容时,会把内容中匹配的前缀字符串去掉。
suffix :当 trim 元素内包含内容时,会给内容增加 suffix 指定的后缀。
suffixOverrides :当 trim 元素内包含内容时,会把内容中匹配的后缀字符串去掉。
6)foreach

SQL 语句中有时会使用 IN 关键字,例如 id in ( 1 , 2 , 3 )。可以使用${ ids }方式直接获取值,但这种写法不能防止 SQL 注入,想避免 SQL 注入就需要用#{}的方式,这时就要配合使用 foreach 标签来满足需求。
foreach 可以对数组、 Map 或实现了iterable 接口(如 List 、 Set )的对象进行遍历。数组在处理时会转换为 List 对象,因此 foreach 遍历的对象可以分为两大类 : Iterable类型和 Map 类型。
7)bind

bind 标签可以使用 OGNL 表达式创建一个变量井将其绑定到上下文中。使用 con cat 函数连接字符串,在 MySQL 中,这个函数支持多个参数,但在 Oracle 中只支持两个参数。由于不 同数据库之间的语法差异 ,如果更换数据库,有些 SQL 语句可能就需要重写。针对这种情况,可 以使用 bind 标签来避免由于更换数据库带来的一些麻烦。

原来的SQL:
<if test=” userName != null and userName ! = ''”>
and user_name like concat ( ’%’, #{userName},’%’ )
</if>

变更后:
<if test=” userName != null and userName !=''”>
<bind name= " userNameLike ” value = ”'%'+ userName + '%'” /〉
and user_name like #{userNameLike}
</if>

6. Mybatis开发基本步骤

1)创建基础配置文件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>
    <typeAliases>
  <!-- 定义单个pojo类别名
		type:类的全路劲名称
		alias:别名
		 -->
    <typeAlias type="com.mybatis.pojo.User" alias="User"/>
  </typeAliases>
	<!-- 和spring整合后 environments配置将废除-->
	<environments default="development">
		<environment id="development">
		<!-- 使用jdbc事务管理-->
			<transactionManager type="JDBC" />
		<!-- 数据库连接池-->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>
<mappers>
<!-- 
		使用class属性引入接口的全路径名称:
		使用规则:
			1. 接口的名称和映射文件名称除扩展名外要完全相同
			2. 接口和映射文件要放在同一个目录下
		 -->
<!-- 		<mapper class="cn.itheima.mapper.UserMapper"/> -->
		
		<!-- 使用包扫描的方式批量引入Mapper接口 
				使用规则:
				1. 接口的名称和映射文件名称除扩展名外要完全相同
				2. 接口和映射文件要放在同一个目录下
		-->
		<!-- <package name="cn.itheima.mapper"/> -->
		<mapper class="com.mybatis.mapper.UserMapper" />
</mappers>
</configuration>

此处的数据库连接配置可存在一个配置文件db.properties中:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

然后修改上面SqlMapConfig.xml相关配置,如下:

<!-- 定义外部数据库配置文件
	 db.properties
		 -->
<properties resource="db.properties"></properties>
  
  <typeAliases>
  <!-- 定义单个pojo类别名
		type:类的全路劲名称
		alias:别名
		 -->
    <typeAlias type="com.mybatis.pojo.User" alias="User"/>
  </typeAliases>
  <environments default="development">
     <environment id="development">
	     <transactionManager type="JDBC"/>
	     <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>

2)创建实体类,如User.java;

Public class User {
	private int id;
	private String username;// 用户姓名
	private String sex;// 性别
	private Date birthday;// 生日
	private String address;// 地址


get/set……
}

3)创建接口

public interface UserMapper {
   
   public User findUserById(Integer id);
   
   public List<User> findUserByUserName(String username);
}

3)创建映射文件,如UserMapper .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接口代理实现编写规则:
	1. 映射文件中namespace要等于接口的全路径名称
	2. 映射文件中sql语句id要等于接口的方法名称
	3. 映射文件中传入参数类型要等于接口方法的传入参数类型
	4. 映射文件中返回结果集类型要等于接口方法的返回值类型
 -->

<mapper namespace="com.mybatis.mapper.UserMapper">
   <!-- 
		id:sql语句唯一标识
		parameterType:指定传入参数类型
		resultType:返回结果集类型
		#{}占位符:起到占位作用,如果传入的是基本类型,那么#{}中的变量名可以随意写
	-->
	<select id="findUserById" parameterType="int" resultType="User">
		select * from user where id=#{id}
	</select>
	
	<!-- 
	   如果返回结果为集合,可以调用selectList方法,这个方法返回的结果就是一个集合,所以映射文件中应该配置成集合泛型的类型
	  ${}拼接符:字符串原样拼接,如果传入的是基本数据类型,那么${}中的变量名必须使用value
	  注意:拼接符有sql注入的风险,所以慎重使用。一般=号后面用占位符,like后面用拼接符
	 -->
	<select id="findUserByUserName" parameterType="string" resultType="User">
	    select * from user where username like '%${value}%'
	</select>
</mapper>

4)测试类

public class UserMapperTest {

	private SqlSessionFactory sqlSessionFactory;//会话工厂
	//初始化
	public void init() throws Exception{
		String resource= "SqlMapConfig.xml";// 配置文件
		InputStream inputStream = Resources.getResourceAsStream(resource); // 通过流将核心配置文件读取进来
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);	// 通过核心配置文件输入流来创建会话工厂	 
	}
	
	public void testFindUserById() throws Exception {
        // 通过工厂创建数据库会话实例sqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
        //获取接口
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		User user = userMapper.findUserById(10);
		System.out.println(user);
	}
	
	public void testFindUserByUserName() throws Exception{
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		List<User> list = userMapper.findUserByUserName("王");
		Iterator<User> iterator = list.iterator();
		while (iterator.hasNext()) {
			User user= iterator.next();
			System.out.println(user.toString());
		}
		
	}
}

5)总结1:
(1)mybatis是一个持久层框架, 作用是跟数据库交互完成增删改查
(2)原生Dao实现(需要接口和实现类)
(3)动态代理方式(只需要接口)
    mapper接口代理实现编写规则:

  • 映射文件中namespace要等于接口的全路径名称
  • 映射文件中sql语句id要等于接口的方法名称
  • 映射文件中传入参数类型要等于接口方法的传入参数类型
  • 映射文件中返回结果集类型要等于接口方法的返回值类型

(4) #{}占位符:占位
    如果传入的是基本类型,那么#{}中的变量名称可以随意写
    如果传入的参数是pojo类型,那么#{}中的变量名称必须是pojo中的属性.属性.属性...

(5) ${}拼接符:字符串原样拼接
    如果传入的是基本类型,那么${}中的变量名必须是value
    如果传入的参数是pojo类型,那么${}中的变量名称必须是pojo中的属性.属性.属性...
    注意:使用拼接符有可能造成sql注入,在页面输入的时候可以加入校验,不可输入sql关键字,不可输入空格
(6)映射文件:

  • 传入参数类型通过parameterType属性指定
  • 返回结果集类型通过resultType属性指定

(7)hibernate和mybatis区别:
    hibernate:它是一个标准的orm框架,比较重量级,学习成本高.
        优点:高度封装,使用起来不用写sql,开发的时候,会减低开发周期.
        缺点:sql语句无法优化
        应用场景:oa(办公自动化系统), erp(企业的流程系统)等,还有一些政府项目,
            总的来说,在用于量不大,并发量小的时候使用.
    mybatis:它不是一个orm框架, 它是对jdbc的轻量级封装, 学习成本低,比较简单
        有点:学习成本低, sql语句可以优化, 执行效率高,速度快
        缺点:编码量较大,会拖慢开发周期
        应用场景: 互联网项目,比如电商,P2p等
             总的来说是用户量较大,并发高的项目.

7. 动态SQL扩展

1) 在接口映射文件如上面的UserMapper .xml,使用where  if 时,可如下将通用的sql封装,即:

<mapper namespace="cn.itheima.mapper.UserMapper">
	<!-- 封装sql条件,封装后可以重用. 
	id:是这个sql条件的唯一标识 -->
	<sql id="user_Where">
		<!-- where标签作用:
				会自动向sql语句中添加where关键字
				会去掉第一个条件的and关键字
			 -->
		<where>
			<if test="username != null and username != ''">
				and username like '%${username}%'
			</if>
			<if test="sex != null and sex != ''">
				and sex=#{sex}
			</if>
		</where>
	</sql>
<select id="findUserByUserNameAndSex" parameterType="cn.itheima.pojo.User" resultType="cn.itheima.pojo.User">
		select * from user 
		
		<!-- 调用sql条件 refid参数值为封装sql条件的id-->
		<include refid="user_Where"></include>
	</select>
</mapper>

2)在接口映射文件如上面的UserMapper .xml,使用foreach,如下:

<select id="findUserByIds" parameterType="com.mybatis.pojo.QueryVo" resultType="User">
	    select * from user
	    <where>
	       <if test="ids != null">
	       <!-- 
				foreach:循环传入的集合参数
				collection:传入的集合的变量名称
				item:每次循环将循环出的数据放入这个变量中
				open:循环开始拼接的字符串
				close:循环结束拼接的字符串
				separator:循环中拼接的分隔符
				 -->
	         <foreach collection="ids" item="id" open="id in (" close=")" separator=",">
	            #{id}
	         </foreach>
	       </if>
	    </where>
	</select>

其中:在com.mybatis.pojo.QueryVo这个类中定义了一个List集合ids用于存储id,即private List<Integer> ids;

8. 单个对象的映射关系(即一对一的关系)

有两种方式:

 1)自动映射:在接口映射文件如上面的UserMapper .xml中如下配置:

<!-- 一对一:自动映射 -->
	<select id="findOrdersAndUser1" resultType="cn.itheima.pojo.CustomOrders">
		select a.*, b.id uid, username, birthday, sex, address 
		from orders a, user b 
		where a.user_id = b.id
	</select>


order表的实体类如下:
public class Orders {
    private Integer id;

    private Integer userId;

    private String number;

    private Date createtime;

    private String note;
   
    get...
    set...
}

其中cn.itheima.pojo.CustomOrders这个类是一个继承类,继承了orders表实体类,并包含user表的字段。因为java是单继承,所以不推荐使用该方式。

2)手动映射:在接口映射文件如上面的UserMapper .xml中如下配置:

<!-- 一对一:手动映射 -->
	<!-- 
	id:resultMap的唯一标识
	type:将查询出的数据放入这个指定的对象中
	注意:手动映射需要指定数据库中表的字段名与java中pojo类的属性名称的对应关系
	 -->
	<resultMap type="cn.itheima.pojo.Orders" id="orderAndUserResultMap">
		<!-- id标签指定主键字段对应关系
		column:列,数据库中的字段名称
		property:属性,java中pojo中的属性名称
		 -->
		<id column="id" property="id"/>
		
		<!-- result:标签指定非主键字段的对应关系 -->
		<result column="user_id" property="userId"/>
		<result column="number" property="number"/>
		<result column="createtime" property="createtime"/>
		<result column="note" property="note"/>
		
		<!-- 这个标签指定单个对象的对应关系 
		property:指定将数据放入Orders中的user属性中
		javaType:user属性的类型
		-->
		<association property="user" javaType="cn.itheima.pojo.User">
			<id column="uid" property="id"/>
			<result column="username" property="username"/>
			<result column="birthday" property="birthday"/>
			<result column="sex" property="sex"/>
			<result column="address" property="address"/>
		</association>
	</resultMap>
	<select id="findOrdersAndUser2" resultMap="orderAndUserResultMap">
		select a.*, b.id uid, username, birthday, sex, address 
		from orders a, user b 
		where a.user_id = b.id
	</select>

此种方式在定义orders表的实体类时,需要定义user表的实体类对象,即定义一个字段 private User user; 然后使用get和set方式取值和赋值。 

9. 对集合对象的映射(即一对多的关系)

在接口映射文件如上面的UserMapper .xml中如下配置: 

<resultMap type="cn.itheima.pojo.User" id="userAndOrdersResultMap">
		<id column="id" property="id"/>
		<result column="username" property="username"/>
		<result column="birthday" property="birthday"/>
		<result column="sex" property="sex"/>
		<result column="address" property="address"/>
		
		<!-- 指定对应的集合对象关系映射
		property:将数据放入User对象中的ordersList属性中
		ofType:指定ordersList属性的泛型类型
		 -->
		<collection property="ordersList" ofType="cn.itheima.pojo.Orders">
			<id column="oid" property="id"/>
			<result column="user_id" property="userId"/>
			<result column="number" property="number"/>
			<result column="createtime" property="createtime"/>
		</collection>
	</resultMap>
	<select id="findUserAndOrders" resultMap="userAndOrdersResultMap">
		select a.*, b.id oid ,user_id, number, createtime 
		from user a, orders b where a.id = b.user_id
	</select>

这种映射关系在user表的实体类中需要包含orders表的实体对象集合,即定义一个字段为 private List<Orders> ordersList;然后使用get和set方式取值和赋值。 

10. Spring 与 Mybatis整合

1)整合需要的*.jar包可在:mybatis与spring整合全部jar包(包括springmvc) 进行下载。

2)整合后的核心配置文件ApplicationContext.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<!-- 加载配置文件 -->
	<context:property-placeholder location="classpath:db.properties" />
	<!-- 数据库连接池 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="maxActive" value="10" />
		<property name="maxIdle" value="5" />
	</bean>
	
	<!-- 整合会话工厂归spring管理 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	     <!-- 指定mybatis核心配置文件 -->
	     <property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
	     <!-- 指定会话工厂使用的数据源 -->
	     <property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!--实现方式一: 配置原生Dao实现 
	   注意:class必须指定Dao实现的全路径名称
	-->
	<!-- <bean id="userDao" class="com.sm.dao.UserDaoImpl">
	     <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>
    -->
    <!-- 实现方式二: Mappper接口代理实现	
	 -->
	 <!-- <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
	   <property name="mapperInterface" value="com.sm.mapper.UserMapper1"></property>
	   <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	 </bean> -->
	
	<!-- 使用包扫描的方式批量引入Mapper
	扫描后引用的时候可以使用类名,首字母小写.
	 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	   <!-- 指定要扫描的包的全路径名称,如果有多个包用英文状态下的逗号分隔 -->
	   <property name="basePackage" value="com.sm.mapper"></property>
	</bean>
	
</beans>

3)mybatis的核心配置文件SqlMapConfig.xml就不需要了

4)假如采用的是原生DAO来实现的话,实现类需要进行修改(需继承一个超类SqlSessionDaoSupport), 如下(我在项目中没有采用这种方式,):

package com.sm.dao;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import com.sm.pojo.User;

/**
 * 整合之后实现类需要继承一个超类SqlSessionDaoSupport
 * 之后获取会话直接用this.getSqlSession()
 * @author xudso
 *
 */

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {


	@Override
	public User findUserById(Integer id){
		SqlSession sqlSession = this.getSqlSession();
		User user=sqlSession.selectOne("test.findUserById",id);
		return user;
	}

	@Override
	public List<User> findUserByUserName(String userName) {
		
		SqlSession sqlSession = this.getSqlSession();
		List<User> list = sqlSession.selectList("test.findUserByUserName", userName);
		return list;
	}

	@Override
	public void insertUser(User user) {
		
		SqlSession sqlSession = this.getSqlSession();
		
		sqlSession.insert("test.insertUser", user);
        sqlSession.commit();
	}

	@Override
	public void deletUserById(Integer id) {
		
		SqlSession sqlSession = this.getSqlSession();
		sqlSession.delete("test.delUser", id);
        sqlSession.commit();
	}

	@Override
	public void updateUserById(User user) {
		SqlSession sqlSession = this.getSqlSession();
		sqlSession.update("test.updateUser", user);
        sqlSession.commit();
	}

}

5)采用Mappper接口代理实现的方式(推荐这种方式),即接口类UserMapper1.java和接口映射文件UserMapper1.xml分别如下:

package com.sm.mapper;

import java.util.List;

import com.sm.dao.UserDao;
import com.sm.pojo.QueryVo;
import com.sm.pojo.User;

public interface UserMapper1 {
   
   public User findUserById(Integer id);
   
   public List<User> findUserByUserName(String username);
   
   public List<User> findUserByVo(QueryVo queryVo);
   
   public List<User> findUserByIds(QueryVo queryVo);
}
<?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接口代理实现编写规则:
	1. 映射文件中namespace要等于接口的全路径名称
	2. 映射文件中sql语句id要等于接口的方法名称
	3. 映射文件中传入参数类型要等于接口方法的传入参数类型
	4. 映射文件中返回结果集类型要等于接口方法的返回值类型
 -->

<mapper namespace="com.sm.mapper.UserMapper1">
   <!-- 
		id:sql语句唯一标识
		parameterType:指定传入参数类型
		resultType:返回结果集类型
		#{}占位符:起到占位作用,如果传入的是基本类型,那么#{}中的变量名可以随意写
	-->
	<select id="findUserById" parameterType="int" resultType="com.sm.pojo.User">
		select * from user where id=#{id}
	</select>
	
	<!-- 
	   如果返回结果为集合,可以调用selectList方法,这个方法返回的结果就是一个集合,所以映射文件中应该配置成集合泛型的类型
	  ${}拼接符:字符串原样拼接,如果传入的是基本数据类型,那么${}中的变量名必须使用value
	  注意:拼接符有sql注入的风险,所以慎重使用。一般=号后面用占位符,like后面用拼接符
	 -->
	<select id="findUserByUserName" parameterType="string" resultType="com.sm.pojo.User">
	    select * from user where username like '%${value}%'
	</select>
	
	<select id="findUserByVo" parameterType="com.sm.pojo.QueryVo" resultType="com.sm.pojo.User">
	    select * from user where username like '%${user.username}%' and sex=#{user.sex}
	</select>
	
	<select id="findUserByIds" parameterType="com.sm.pojo.QueryVo" resultType="com.sm.pojo.User">
	    select * from user
	    <where>
	       <if test="ids != null">
	       <!-- 
				foreach:循环传入的集合参数
				collection:传入的集合的变量名称
				item:每次循环将循环出的数据放入这个变量中
				open:循环开始拼接的字符串
				close:循环结束拼接的字符串
				separator:循环中拼接的分隔符
				 -->
	         <foreach collection="ids" item="id" open="id in (" close=")" separator=",">
	            #{id}
	         </foreach>
	       </if>
	    </where>
	</select>
	
	
</mapper>

 5 )编写测试类(这里以 采用Mappper接口代理实现的方式编写测试类):

package com.sm.test;

import javax.jws.soap.SOAPBinding.Use;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.sm.mapper.UserMapper1;
import com.sm.pojo.User;

public class UserMapperTest {

	private ApplicationContext applicationContext;
	
    //初始化
	@Before
	public void setUp() throws Exception{
		String configLocation = "ApplicationContext.xml";
		applicationContext = new ClassPathXmlApplicationContext(configLocation);
	}
	
	@Test
	public void testFindUserById() throws Exception{
		UserMapper1 userMapper1 =(UserMapper1) applicationContext.getBean("userMapper1");
		User user =userMapper1.findUserById(1);
		System.out.println(user);
	}
	
}

11. 逆向工程

1)导入相应的包,可从这里现在:mybatis逆向工程自动生成需要的包

2)配置文件generator.xml(根据自己的需要进行修改)

<?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="root">
		</jdbcConnection>
		<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
			connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
			userId="yycg"
			password="yycg">
		</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.sm.pojo"
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
			<!-- 从数据库返回的值被清理前后的空格 -->
			<property name="trimStrings" value="true" />
		</javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
		<sqlMapGenerator targetPackage="com.sm.mapper" 
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</sqlMapGenerator>
		<!-- targetPackage:mapper接口生成的位置 -->
		<javaClientGenerator type="XMLMAPPER"
			targetPackage="com.sm.mapper" 
			targetProject=".\src">
			<!-- enableSubPackages:是否让schema作为包的后缀 -->
			<property name="enableSubPackages" value="false" />
		</javaClientGenerator>
		<!-- 指定数据库表 -->
<!-- 		<table tableName="items"></table> -->
		<table tableName="orders"></table>
<!-- 		<table tableName="orderdetail"></table> -->
		<table tableName="user"></table>
		<!-- <table schema="" tableName="sys_user"></table>
		<table schema="" tableName="sys_role"></table>
		<table schema="" tableName="sys_permission"></table>
		<table schema="" tableName="sys_user_role"></table>
		<table schema="" tableName="sys_role_permission"></table> -->
		
		<!-- 有些表的字段需要指定java类型
		 <table schema="" tableName="">
			<columnOverride column="" javaType="" />
		</table> -->
	</context>
</generatorConfiguration>

3)启动类StartServer.java。然后运行启动类就会自动生成表的实体类和对应的接口映射文件。

package generator;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

public class StartServer {
	public void generator() throws Exception{
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		File configFile = new File("generator.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 {
			StartServer startServer = new StartServer();
			startServer.generator();
		} catch (Exception e) {
			e.printStackTrace();
		}
}
}

 12. 总结2:

1)输入映射(就是映射文件中可以传入哪些参数类型)

  • 基本类型
  • pojo类型
  • Vo类型

2)输出映射(返回的结果集可以有哪些类型)

  • 基本类型
  • pojo类型
  • List类型

3)动态sql:动态的拼接sql语句,因为sql中where条件有可能多也有可能少

  • where:可以自动添加where关键字,还可以去掉第一个条件的and关键字
  • if:判断传入的参数是否为空
  • foreach:循环遍历传入的集合参数
  • sql:封装查询条件,以达到重用的目的

4)对单个对象的映射关系:

  • 自动关联(偷懒的办法):可以自定义一个大而全的pojo类,然后自动映射其实是根据数据库总的字段名称和pojo中的属性名称对应。
  • 手动关联: 需要指定数据库中表的字段名称和java的pojo类中的属性名称的对应关系;使用association标签。

5)对集合对象的映射关系
    只能使用手动映射:指定表中字段名称和pojo中属性名称的对应关系;使用collection标签。
6)spring和mybatis整合
    整合后会话工厂都归spring管理
    方式1:原生Dao实现:

  • 需要在spring配置文件中指定dao实现类
  • dao实现类需要继承SqlSessionDaoSupport超类
  • 在dao实现类中不要手动关闭会话,不要自己提交事务.

    方式2:Mapper接口代理实现:
        在spring配置文件中可以使用包扫描的方式,一次性的将所有mapper加载

7)逆向工程:自动生成Pojo类,还可以自动生成Mapper接口和映射文件
    注意:生成的方式是追加而不是覆盖,所以不可以重复生成,重复生成的文件有问题。如果想重复生成将原来生成的文件删除。

猜你喜欢

转载自blog.csdn.net/xudasong123/article/details/80824033