Mybatis

Mybatis

1.Mybatis简介

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)

1.1.Mybatis框架的意义

  1. 简化了JDBC开发.(JDBC :获取连接 数据操作对象 执行sql语句 处理结果集 释放资源,以上步骤是重复的事情),提高了开发效率。

  2. Mybatis统一数据库操作规范。有利于代码的维护。

  3. Mybatis中强大的映射功能,支持各种复杂的查询。

  4. Mybatis中动态SQL的功能,简化了条件查询

  5. 简单易学,本身就很小且简单。没有任何第三方依赖。

  6. 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。

  7. 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

  8. 提供映射标签,支持对象与数据库的orm字段关系映射,mybatis也属于ORM框架。

1.2.ORM关系映射

ORM框架Hibernate Mybatis Spring DATA JPA 等等

ORM :

​ O :Object

​ R : Relational

​ M : Mapping

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据的关系.

将数据库中的数据与程序中对应进行绑定。最终可实现,操作对象及可操作数据库。

在这里插入图片描述

2.Mybatis使用

2.1.Mybatis入门使用

2.1.1.下载mybatis相关jar包

mybatis-x.x.x.jar

2.1.2.在项目中引入mybatis核心jar包

2.1.3.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>
  <!-- 运行环境  -->
  <!-- default : 采用环境 -->
  <environments default="dev">
  	<!-- 具体的环境  id 具体环境唯一标识-->
    <environment id="dev">
    	<!-- 事务管理器   -->
      <transactionManager type="JDBC"/>
      <!-- 数据源 -->
      <dataSource type="POOLED">
      	<!-- 驱动 -->
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/1009_cms"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
      </dataSource>
    </environment>
    <!-- 测试环境要接近生产环境   尽可能和生产环境保持一致 -->
    <environment id="test">
      <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}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
  	<!-- 具体的映射器 -->
  	<!-- 注解   xml(重点) -->
    <mapper resource="UserMapper.xml"/>
  </mappers>
</configuration>

2.1.4.映射配置文件

<?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">
  <!-- 
  	namespace : 命名空间
   -->
<mapper namespace="com.zw.pojo.User">
	<!-- 
		查询指令标签
		id : 查询指令的唯一标识	
		resultType : 查询结果映射的类 
	 -->
  	<select id="selectOne" resultType="com.zw.pojo.User">
  		select id as id,user_name as userName,password as password,real_name as realName  from c_user where id = 1
  	</select>
  
  	<select id="selectAll" resultType="com.zw.pojo.User">
  		select id as id,user_name as userName,password as password,real_name as realName  from c_user
  	</select>
  
</mapper>

2.1.5.dao

package com.zw.dao;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.zw.pojo.User;

public class UserDao {
	
	public User selectOne() {
		User user = null;
		try {
			//InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			// 1. 找到配置文件
			InputStream inputStream = UserDao.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
			// 2. 根据配置文件生成数据库会话对象
			//获取SqlSessionFactory构建器
			SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
			// 解析配置文件  构建一个SqlSession 工厂
			SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
			// 根据SqlSession 工厂 获取SqlSession会话对象
			SqlSession sqlSession = sqlSessionFactory.openSession();
			// 3. 使用会话对象操作数据库
			user = sqlSession.selectOne("com.zw.pojo.User.selectOne");
			System.out.println(user);
			//4.释放资源
			sqlSession.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return user;
	}

	/**
	 * @Title: selectAll
	 * @author: KevinZeng   
	 * @date: 2019年12月28日 上午10:46:05 
	 * @Description: 查询所有
	 * @return
	 * @return: List<User>
	 */
	public List<User> selectAll(){
		//1. 找到资源文件
		List<User> users = null;
		try {
			InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
			//2.创建SqlSessionFactory构建器
			SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
			//3. 根据配置文件构建SqlSessionFactory
			SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
			//4. 获取数据库会话对象
			SqlSession sqlSession = sqlSessionFactory.openSession();
			//5. 进行数据操作
			users = sqlSession.selectList("com.zw.pojo.User.selectAll");
			for (User user : users) {
				System.out.println(user);
			}
			sqlSession.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return users;
	}
	
	
	public static void main(String[] args) {
		new UserDao().selectAll();
	}
}

2.2.mybatis普通CRUD

在这里插入图片描述

<?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">
  <!-- 
  	namespace : 命名空间
  
   -->
<mapper namespace="com.zw.pojo.User">

	<insert id="insert" parameterType="com.zw.pojo.User">
		insert into user (name,password) value (#{name},#{password})
	</insert>
	
	<delete id="delete" parameterType="int">
		delete from  user where id = #{id}
	</delete>
  
  	<select id="selectOne" resultType="com.zw.pojo.User">
  		select id,name,password from user where id = #{id}
  	</select>
  	
  	<select id="selectAll" resultType="com.zw.pojo.User">
  		select id,name,password from user
  	</select>
  	
  	<update id="update"  parameterType="com.zw.pojo.User">
  		update user set name = #{name} ,password = #{password}
  		where id = #{id}
  	</update>
</mapper>
package com.zw.dao.impl;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.zw.dao.IUserDao;
import com.zw.pojo.User;
import com.zw.util.SqlSessionUtil;

public class UserDaoImpl implements IUserDao {

	@Override
	public int insert(User user) {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		int m = sqlSession.insert("com.zw.pojo.User.insert",user);
		//在使用  mybatis  进行数据更新操作时   mybatis默认是不会提交事务
		//需要手动提交
		sqlSession.commit();
		sqlSession.close();
		return m;
	}

	@Override
	public int delete(Integer id) {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		int m = sqlSession.delete("com.zw.pojo.User.delete",id);
		sqlSession.commit();
		sqlSession.close();
		return m;
	}

	@Override
	public User selectOne(Integer id) {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		User user = sqlSession.selectOne("com.zw.pojo.User.selectOne",id);
		sqlSession.close();
		return user;
	}

	@Override
	public List<User> selectAll() {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		List<User> users = sqlSession.selectList("com.zw.pojo.User.selectAll");
		sqlSession.close();
		return users;
	}

	@Override
	public int update(User user) {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		int m = sqlSession.update("com.zw.pojo.User.update", user);
		sqlSession.commit();
		sqlSession.close();
		return m;
	}

	
	public static void main(String[] args) {
		IUserDao userDao = new UserDaoImpl();
		List<User> users = userDao.selectAll();
		System.out.println(users);
		System.out.println(userDao.selectOne(2));
		User user = new User("456", "456");
		//添加
		//userDao.insert(user);
		//删除
		//userDao.delete(4);
		//修改
		user = new User("110", "110");
		user.setId(2);
		userDao.update(user);
		
	}
}

2.3.Mybatis接口式进行CRUD

在这里插入图片描述
2.3.1.映射文件中的namespace的值:CRUD接口的全路径

<mapper namespace="com.zw.dao.IUserDao">

写错时,会出现以下异常:
在这里插入图片描述
2.3.2.映射文件中命令标签ID值要接口中的方法名一致

若不一致会出现以下异常:
在这里插入图片描述

<?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">
  <!-- 
  	namespace : 命名空间
   -->
<mapper namespace="com.zw.dao.IUserDao">

	<insert id="insert" parameterType="com.zw.pojo.User">
		insert into user (name,password) value (#{name},#{password})
	</insert>
	
	<delete id="delete" parameterType="int">
		delete from  user where id = #{id}
	</delete>
  
  	<select id="selectOne" resultType="com.zw.pojo.User">
  		select id,name,password from user where id = #{id}
  	</select>
  	
  	<select id="selectAll" resultType="com.zw.pojo.User">
  		select id,name,password from user
  	</select>
  	
  	<update id="update"  parameterType="com.zw.pojo.User">
  		update user set name = #{name} ,password = #{password}
  		where id = #{id}
  	</update>
</mapper>
package com.zw.dao;

import java.util.List;

import com.zw.pojo.User;

public interface IUserDao {
	
	/**
	 * @Title: insert
	 * @author: KevinZeng   
	 * @date: 2019年12月28日 上午11:43:59 
	 * @Description: 新增用户
	 * @param user
	 * @return
	 * @return: int
	 */
	public int insert(User user);
	/**
	 * @Title: delete
	 * @author: KevinZeng   
	 * @date: 2019年12月28日 上午11:44:17 
	 * @Description: 根据ID 删除用户
	 * @param id
	 * @return
	 * @return: int
	 */
	public int delete(Integer id);
	/**
	 * @Title: selectOne
	 * @author: KevinZeng   
	 * @date: 2019年12月28日 上午11:44:40 
	 * @Description: 根据ID 查询用户
	 * @param id
	 * @return
	 * @return: User
	 */
	public User selectOne(Integer id);
	/**
	 * @Title: selectAll
	 * @author: KevinZeng   
	 * @date: 2019年12月28日 上午11:45:14 
	 * @Description: 查询所有用户
	 * @param user
	 * @return
	 * @return: List<User>
	 */
	public List<User> selectAll();
	/**
	 * @Title: update
	 * @author: KevinZeng   
	 * @date: 2019年12月28日 上午11:45:29 
	 * @Description: 修改用户信息
	 * @param user
	 * @return
	 * @return: int
	 */
	public int update(User user);
	
	
}

package com.zw.test;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.zw.dao.IUserDao;
import com.zw.pojo.User;
import com.zw.util.SqlSessionUtil;

public class Test {
	
	public static void main(String[] args) {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        //获取接口的代理类
		IUserDao userDao = sqlSession.getMapper(IUserDao.class);
		//查询所有数据
		List<User> users = userDao.selectAll();
		System.out.println(users);
		User user = userDao.selectOne(1);
		System.out.println(user);
		
		//userDao.insert(new User("789", "789"));
		
		// userDao.delete(6);
		//User user1 = new User("Hello", "Mybatis");
		//user1.setId(2);
		//userDao.update(user1);
		sqlSession.commit();
		sqlSession.close();
		
	}

}

3.Mybatis配置文件

3.1.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">
  <!-- 根标签  表示mybatis 运行环境配置-->
  <configuration>
      //一、properties标签
  	<!--
		mybatis可以使用properties来引入外部properties配置文件的内容
		resource:引入类路径下的资源
		url:引入网络路径或磁盘路径下的资源
	-->
  	<properties resource="jdbc.properties"></properties>
      
      //二、settings标签
  	<!-- 
		settings包含很多重要的设置项
		setting:用来设置每一个设置项
			name:设置项名
			value:设置项取值
	-->
  	<settings>
              <setting name="mapUnderscoreToCamelCase" value="true"/>
  		<setting name="logImpl" value="LOG4J"/>
  	</settings>
  	
      //三、typeAliases标签:别名处理器,可以为我们的java类型起别名
                别名不区分大小写
      <typeAliases>
      <!-- 
  		typeAliases:为某个java类型起别名
			type:指定要起别名的类型全类名;默认别名就是类名小写
			alias:指定新的别名
	-->
      <!--为指定的类 指定别名-->
      <!--<typeAliases type="com.sxt.pojo.Employee" alias="emp"/>-->
      <!--
		package:为某个包下的所有类批量起别名
			name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认的别名,别名就是类名
     -->
          <package name="com.zw.bean"/>
          <!--批量起别名的情况下,使用@Alias注解为某个类型指定新的别名-->
  	</typeAliases>
      
  	//四、typeHandlers标签:类型处理器,解决java属性和数据库字段类型兼容问题
  	<typeHandlers>
        	//了解即可
      </typeHandlers>
      
      
      //五、plugins标签:mybatis插件配置
      		拦截以下四大对象
		-  Executor执行处理器(update,query,flushStatements,commit,rollback.getTransaction,close,isClosed)
      	      -  ParameterHandler参数处理器(getParameterObject,setParameters)
      	      -  ResultSetHandler结果集处理器(handlerResultSets,handlerOutputParameters)
      	      -  StatementHandler 处理sql的处理器(prepare,parameterize,batch,update,query)
  	<!-- 分页插件  -->
  	<!-- 
	  	<plugins>
	  		<plugin interceptor="拦截器的全路径"></plugin>
	  	</plugins> 
  	-->
     
     //六、environments标签:环境们,mybatis可以配置多种环境,default指定使用某种环境,可以快速切换
      		environment:配置一个具体的环境信息:必须有2个标签;id代表当前环境的唯一标识
      			transactionManager:事务管理器
      				type:事务管理器的类型,mybatis提供2种:
                                                    | JDBC(JdbcTransactionFactory)  使用JDBC的事务管理器  提交或者回滚事务
                                                    | MANAGED(ManagedTransactionFactory) 从不提交和回滚事务
                                            自定义事务管理器:实现TransactionFactory接口,type指定为全类名
      			dataSource:数据源;
      				type:数据源类型,mybatis内置3种:
      						| UNPOOLED(UnpooledDataSourceFactory)每次都创建新的连接。不是用连接池
      						| POOLED(PooledDataSourceFactory)使用连接池    推荐
      						| JNDI(JndiDataSourceFactory)应服务器连接  不使用
      					  自定义数据源:实现DataSourceFactory接口,type是全类名
  <environments default="development">
  	<!-- 具体的环境  id 具体环境唯一标识-->
          <environment id="dev">  
              	<!--事务管理器 -->
              	<transactionManager type="JDBC"/>
              	<!-- 数据源-->
              	<dataSource type="POOLED">
                  	<!-- 驱动 -->
        	     <property name="driver" value="com.mysql.jdbc.Driver"/>
        	     <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"/>
        	     <property name="username" value="root"/>
             	  <property name="password" value="root"/>
                  </dataSource>
      	</environment>
  </environments>

  //七、databaseIdProvider支持多数据库厂商的;
      	type="DB_VENDOR"  :VendorDatabaseIdProvider
      	作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql
          MySQL,Oracle,SQL Server,xxxx
      	然后在mybatis映射文件sql语句的标签里面指定databaseId的别名即可
   <databaseIdProvider type="DB_VENDOR">
    	<!--为不同的数据库厂商起别名-->  
       		<property name="MySQL" value="mysql"/>
       		<property name="Oracle" value="oracle"/>
       		<property name="SQL Server" value="sqlserver"/>
    </databaseIdProvider>
      
   //八、mappers:将sql映射注册到全局配置中
  <mappers>
  	<!--
		mapper:注册一个sql映射
			resource:引用类路径下的sql映射文件
				mybatis/mapper/EmployeeMapper.xml
			url:引用网路路径或者磁盘路径下的sql映射文件
				file:///var/mapper/AuthorMapper.xml
			class :使用类的完全限定名  要求: 配置文件名称和接口名称一致且同包
			<package name="org.mybatis.builder"/> : 将包内的映射器接口实现全部注册为映射器 
	-->
      <!-- 具体的映射器 -->
      <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
  </mappers>
  </configuration>

3.2.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">
  <!-- 
  	namespace : 命名空间
  	namespace作用: 
  		1.namespace + id 生成key,namespace 区分sql命令  sql命令的ID 不能重复
  		2.使用代理时 根据namespace 生成代理类
   -->
<mapper namespace="com.zw.dao.IUserDao">
	<!--  
		cache : 表示开启二级缓存
	 -->
	<!-- 结果映射关系 
		配置映射关系  
		列和属性的关系  解决列名和属性名不一致
	 -->
	<resultMap type="User" id="BASE_RESULT_MAP">
		<result column="id" property="id"/>
		<result column="userName" property="name"/>
		<result column="password" property="password"/>
	</resultMap>
	<!-- 
		sql片段
		将公共的sql 抽离出来 
		可以在多处引用
	 -->
	<sql id="BASE_CLOUNM">
		id,name,password
	</sql>
	<!-- 
		id : 当前namespace中唯一标识
		parameterType : 参数类型  可以缺省  : map
		parameterMap : 已经废弃
		useGeneratedKeys : 是否使用JDBC内部生成主键
		keyColumn : 生成自增涨主键的列名
		keyProperty : 列对应类中属性名
	 -->
	<insert id="insert" keyColumn="id" keyProperty="id"  useGeneratedKeys="true"  parameterType="com.zw.pojo.User">
		insert into user (name,password) value (#{name},#{password})
	</insert>
	
	<delete id="delete" parameterType="int">
		delete from  user where id = #{id}
	</delete>
  	<!-- 
  		id : 当前 namespace中唯一标识
  		parameterType : 参数类型 可缺省
  		resultType : 查询结果对应的类
  		resultMap : 查询结果 对应的映射关系
  		 注意:resultType 和  resultMap 只能使用1个,并且select标签必须有其中一个
  	 -->
  	<select id="selectOne"   resultMap="BASE_RESULT_MAP">
  		select 
  			<include refid="BASE_CLOUNM" />
  		from user where id = #{id}
  	</select>
  	
  	<select id="selectAll" resultType="com.zw.pojo.User">
  		select id,name,password from user
  	</select>
  	
  	<update id="update"  parameterType="com.zw.pojo.User">
  		update user set name = #{name} ,password = #{password}
  		where id = #{id}
  	</update>
</mapper>

3.3.模糊查询

方式一:concat函数(推荐)

	<select id="selectOne" parameterType="String" resultType="User">
		select id,name,password from user where name like concat('%',#{name},'%');
	</select>

方式二:

	<select id="selectOne2" parameterType="String" resultType="User">
		select id,name,password from user where name like '%${name}%';
	</select>

方式三:bind标签

	<select id="selectOne5" parameterType="String" resultType="User">
        <!--
			name 别名
			value : 要查询字符串 预处理	
		-->
		<bind name="keywords" value="'%'+ new String(name) + '%'"/>
		select id,name,password from user where name like #{keywords};
	</select>

3.4.#号与$比较

在mybatis中,使用#{}会对sql语句进行预处理,而$则是字符串拼接。#{}的sql,在一定程度上会防止sql注入攻击。而$,则只是字符串拼接,无法防止sql注入攻击。

一般情况下,能使用#{}就不要使用${}.

由于,#{}使用PreparedStatement设置参数,会对字符串进行转义,前后拼接单引号。此时这个字符串就是一个字符串。无法表示列名。所以#{}这种方式,是不支持数据库关键字相关操作设置。

而${}是字符串拼接,不会对字符串进行特殊处理。所以可以支持数据库关键字相关操作设置。

3.5.分页查询

方式一:使用阿里分页插件:PageHelper

  1. 引入相关jar包:

    pagehelper-5.1.10.jar
    jsqlparser-2.1.jar
    
  2. 在核心配置文件中,配置分页插件

    	<plugins>
    	  		<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    	 </plugins> 
    
  3. 开启分页

    		//开启分页 永远只对最近一次查询生效
    		//若下次查询也需要进行分页查询 需要重新开启
    		Page<User> pageInfo = PageHelper.startPage(1, 10);
    		userDao.selectPage("0");
    		System.out.println("当前页码:"+pageInfo.getPageNum());
    		System.out.println("最大页数"+pageInfo.getPages());
    		System.out.println("符合条件数据行数:"+pageInfo.getTotal());
    		System.out.println("每页数据行数:"+pageInfo.getPageSize());
    		System.out.println("查询具体数据:"+pageInfo.getResult());
    		for (User user : pageInfo.getResult()) {
    			System.out.println(user);
    		}
    
    <select id="selectPage" parameterType="String" resultType="User">
    		select id,name,password  from user where name like  concat('%',#{name},'%')
    </select>
    

    方式二:RowBounds进行分页

    <select id="selectPage3" parameterType="String" resultType="User">
    		select id,name,password  from user where name like concat('%',#{name},'%')
    </select>
    
    public List<User> selectPage3(RowBounds rowBounds,String name);
    
    RowBounds rowBounds = new RowBounds(0,10);
    userDao.selectPage3(rowBounds,"0");
    

3.6.ResultMap

结果映射,由于命名的原因,表的列名往往与Java类中属性名不一致,而Mybatis 默认是根据查询结果的列名查找对应的类的属性名,从而将该列的值,设置到类对象的该属性中。但是,若不一致,则该属性没有值。

解决这个问题,主要两种方案:

  1. 使用as 关键字,为列取别名,保持别名与类中属性名一致。
  2. 使用ResultMap指定列和属性的映射关系
<!-- 
		type : 具体的类的全路径
		id : resultMap的唯一标识
			|-- <id/>  表示主键 查询结果的主键的列   只是标识这个列是主键列
				  |-- column : 列名
				  |-- property : 对应type的属性名
			|-- <result/> 表查询结果的一个列 
				   |-- column : 列名
				   |-- property : 对应type的属性名	  
				  		
	 -->
	<resultMap type="com.zw.pojo.User" id="BASE_RESULT_MAP">
		<id  column="id"  property="id"/>
		<result column="name" property="name"/>
		<result column="password" property="password"/>
	</resultMap>
	
	<select id="selectOne" parameterType="String" resultMap="BASE_RESULT_MAP">
		select id,name,password from user where name like concat('%',#{name},'%')
	</select>

3.7.sql片段

在mybatis中,为了提高sql的复用性。mybatis提供了sql标签,可以将需要复用的sql语句包裹起来,然后在需要使用的地方直接引入这个sql片段即可。

	<sql id="BASE_ALL_COLUMN">
		id,name,password 
	</sql>
	<select id="selectList" parameterType="String" resultMap="BASE_RESULT_MAP">
		select
			<include refid="BASE_ALL_COLUMN"></include>
		 from user where name like concat('%',#{name},'%')
	</select>

注意: 使用include引入这个sql片段

3.8.动态sql

简化sql根据不同的条件生成不同的sql的问题。

例如 :根据ID 更新用户 : User 对象 里面值 生成响应sql语句 动态sql

name : update user set name = “新name” where id = id

name 和 sex : update user set name = “新name” , sex = “新sex” where id = id

update user set name = “新name” ,sex = “新sex”,age = “新age” where id = id值 :// user 里面 只有ID 值 name值

//本来 只需要一步操作 : update

//1. 根据ID 查询用户

//2. 设置用户需要修改的属性值

//3.更新

mybatis为了实现以上功能,提供了以下标签:

3.8.1.if标签

<if test = "表达式"  >
    sql语句
</if>
若如果表达式最终运算结果为true,则将sql语句进行拼接,从而进行执行

示例:

<select id="selectList" resultMap="BASE_RESULT_MAP" parameterType="Student">
		select 
			<include refid="BASE_ALL_COLUMN" />
		from 
			student
		where 1=1
			<if test="id != null and id !=''">
				and id = #{id}
			</if>
			<if test="name != null and name !=''">
				and name like concat('%',#{name},'%')
			</if>
			<if test="minAge != null and minAge !=''">
				and age > #{minAge}
			</if>
			<if test="maxAge != null and maxAge !=''">
				and #{maxAge} > age
			</if>	
	</select>

**注意:**如果不支持字符特殊符号,可以使用:<![CDATA[ 字符串… ]]>转义

3.8.2.choose(when … otherwise)

多条件分支判断,自上而下判断,判断上面条件后执行相应的sql,后面的条件表达式不会运行。若所有when中的表达式都不成立,则执行otherwise的程序

<select id="selectList2" resultMap="BASE_RESULT_MAP">
		select 
			<include refid="BASE_ALL_COLUMN" />
		from 
			student
		where 1=1
			<choose>
				<when test="age != null and age > 10">
					age > 10
				</when>
				<when test="age != null and age > 8">
					age > 8
				</when>
				<otherwise>
					age > 0
				</otherwise>
			</choose>	
	</select>
/** 
* @Title: selectList2
* @author: KevinZeng    
* @date: 2019年12月30日 下午11:55:14 
* @Description: 根据条件查询
* @param student
* @return
* @return: List<Student>
*/
public List<Student> selectList2(Map<String,Object> student);  

Map<String, Object> param = new HashMap<>();
param.put("age", 11);
List<Student> students = studentDao.selectList2(param);
for (Student student : students) {
    System.out.println(student);
}
sqlSession.commit();
sqlSession.close();

3.8.3.where

由于在mybatis动态sql中,第一个条件可能成立也可能不成立,所以后面条件都需要and关键字。此时拼接sql语句,会出现可能多一个and。mybatis 为了解决该问题,提供了where标签,where用于取代where关键字。

被where标签包裹的条件成立,则自动在sql后面追加where关键字 且会去除第一个and 关键字。

<select id="selectList3" resultMap="BASE_RESULT_MAP">
		select 
			<include refid="BASE_ALL_COLUMN" />
		from 
			student
			<where>
				<choose>
					<when test="age != null and age > 10">
						and age > 10
					</when>
					<when test="age != null and age > 8">
						and  age > 8
					</when>
				</choose>	
			</where>
	</select>

3.8.4.set

set标签取代sql语句中的set关键字,自动添加set关键字 ,且去除最后一个逗号。

<update id="update">
		update student 
			<set>
				<if test="name != null and name !=''">
					name = #{name},
				</if>
				<if test="age != null and age !=''">
					age = #{age},
				</if>
				<if test="sex != null and sex !=''">
					sex = #{sex},
				</if>
				<if test="phone != null and phone !=''">
					phone = #{phone},
				</if>
			</set>
			where id = #{id}	
	</update>

3.8.5.foreach

循环标签。

<select id="selectList4" resultMap="BASE_RESULT_MAP">
		select <include refid="BASE_ALL_COLUMN" />
		from student 
		<!-- 
			collection : 待循环集合
			item : 每次循环的集合元素别名
			open : 循环前 字符串
			close : 循环后的字符串
			separator : 每次中间字符串
			index :下标索引
		 -->
		<foreach collection="ids"  item="id"   open=" where 1=1 and  id in("  close=" ) and 1=1"  separator=",">
			#{id}
		</foreach>
	</select>

3.8.6.trim

prefix : 新增前缀
prefixOverrides : 去除的前缀
suffix : 新增的后缀
suffixOverrides : 去除的后缀

<update id="update2">
		update student 
			<!-- 
				prefix : 新增前缀
				prefixOverrides : 去除的前缀
				suffix : 新增的后缀
				suffixOverrides : 去除的后缀
			 -->
			<trim prefix="set"  prefixOverrides="and"    suffixOverrides="and|or">
				<if test="name != null and name !=''">
					and name = #{name}  or
				</if>
				<if test="age != null and age !=''">
					age = #{age},
				</if>
				<if test="sex != null and sex !=''">
					sex = #{sex},
				</if>
				<if test="phone != null and phone !=''">
					phone = #{phone},
				</if>
			</trim>
			where id = #{id}	
	</update>

3.9.联表查询

多张表联合查询。多张表联合查询的结果数据的处理方式。由于一般,一张表对应一个类,但是联表查询时,往往是多张表的数据(多张表的列名),而其没有具体的对应的类。所以其查询的结果需要特殊处理。一般可以使用以下方式处理:

3.9.1.在类中扩展属性

C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.javaStudent.java

public class Classes {
	private Integer id;
	private String name;
}
public class Student {
	private Integer id;
	private String name;
	private Integer age;
	private String sex;
	private String phone;	
	private Integer classId;
	private String className;
}
//省略getters and setters  和 toString 构造

S t u d e n t M a p p e r . j a v a StudentMapper.java StudentMapper.java

public interface StudentMapper {
	public List<Student> selectAll();	
}

S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml

<mapper namespace="com.zw.mapper.StudentMapper">
	<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
		<result column="age" property="age"/>
		<result column="sex" property="sex"/>
		<result column="phone" property="phone"/>
		<result column="class_id" property="classId"/>
		<result column= "className" property="className"/>
	</resultMap>
	
	<sql id="BASE_ALL_COLUMN">
		id,name,age,sex,phone,
	</sql>
	
	<!--查询所有的学生信息包括所有的班级信息-->
	<select id="selectAll" resultMap="BASE_RESULT_MAP">
		SELECT
			st.id,
			st.name,
			st.age,
			st.sex,
			st.phone,
			cs.name  as className
		FROM
			student st
			LEFT JOIN classes cs ON st.class_id = cs.id;
	</select>
</mapper>

3.9.2.使用Map接收查询结果

C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.javaStudent.java

public class Classes {
	private Integer id;
	private String name;
}
public class Student {
	private Integer id;
	private String name;
	private Integer age;
	private String sex;
	private String phone;
}
//省略getters and setters  和 toString 构造

S t u d e n t M a p p e r . j a v a StudentMapper.java StudentMapper.java

public interface StudentMapper {
	public List<Map<String,Object>> selectAll();// 使用Map接收查询结果
}  

S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml

<mapper namespace="com.zw.mapper.StudentMapper">
	<!--  查询所有的学生信息包括其班级信息 使用Map接收查询结果   -->
	<select id="selectAll" resultType="map">
		SELECT
			st.id,
			st.name,
			st.age,
			st.sex,
			st.phone,
			cl.name  as className
		FROM
			student st
			LEFT JOIN classes cl ON st.class_id = cl.id;
	</select>
</mapper>

测 试 类 测试类

public static void main(String[] args) {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Map<String, Object>> students = mapper.selectAll();
    System.out.println(students);
    sqlSession.close();
}

3.9.3.二次查询

先查学生,然后根据班级ID查询班级
C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.javaStudent.java

public class Student {
	private Integer id;
	private String name;
	private Integer age;
	private String sex;
	private String phone;
	private Integer classId;
	private String className;
}
public class Classes {
	private Integer id;
	private String name;
}
//省略getters and setters  和 toString 构造

S t u d e n t M a p p e r . j a v a 和 C l a s s e s M a p p e r . j a v a StudentMapper.java 和 ClassesMapper.java StudentMapper.javaClassesMapper.java

public interface ClassesMapper {
	public Classes selectById(@Param("id")Integer id);//根据班级ID查询班级信息
}	
public interface StudentMapper {
	public List<Student> selectAll();
}

C l a s s e s M a p p e r . x m l ClassesMapper.xml ClassesMapper.xml

<mapper namespace="com.zw.mapper.ClassesMapper">
	<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
	</resultMap>
	
	<select id="selectById" resultMap="BASE_RESULT_MAP">
		SELECT 
			id,name
		FROM
			classes
		WHERE
			id = #{id}
	</select>
</mapper>

S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml

<mapper namespace="com.zw.mapper.StudentMapper">
	<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
		<id column="id" property="id" />
		<result column="name" property="name" />
		<result column="age" property="age" />
		<result column="sex" property="sex" />
		<result column="phone" property="phone" />
		<result column="class_id" property="classId"/>
	</resultMap>
	<sql id="BASE_ALL_COLUMN">
		<!-- 数据库中student表的各个字段 -->
		id, name, age, sex, phone,class_id
	</sql>

	<!--先查询所有的学生 根据学生中班级ID 查询班级信息 -->
	<select id="selectAll" resultMap="BASE_RESULT_MAP">
		SELECT
			<include refid="BASE_ALL_COLUMN"></include>
		FROM 
			student
	</select>
</mapper>

测 试 类 测试类

package com.zw.test;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.zw.mapper.ClassesMapper;
import com.zw.mapper.StudentMapper;
import com.zw.pojo.Classes;
import com.zw.pojo.Student;
import com.zw.util.SqlSessionUtil;

/** 
 * @ClassName: Test 
 * @Description:封装查询结果
 * @author: KevinZeng
 * @date: 2020年1月2日 上午1:00:24  
 */
public class Test {

	public static void main(String[] args) {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
		ClassesMapper classMapper = sqlSession.getMapper(ClassesMapper.class);
		//使用map接收查询结果
		List<Student> sts = studentMapper.selectAll();
		for (int i = 0; i < sts.size(); i++) {
			Student st = sts.get(i); //  6个  
			Integer classId = st.getClassId(); // 班级ID 
			Classes classes = classMapper.selectById(classId);
     //根据班级ID 进行查询班级   一般而言  6 次 
	//然后班级只有3个  所以最多只查询3次  因为已经查询过的数据 放入了缓存中 会先从缓存中检查是否存在对应的数据
	//若存在 则直接从缓存中取  不会进行查询。
		    sts.get(i).setClassName(classes.getName());
			System.out.println(st);
		}
	}
}

3.9.4.mybatis解决方案

在mybatis中,表和表之间存在联系,数据与数据存在关系。多对一,一对多,多对多。这些数据的关系,只是角度不同。基于这一点,mybatis,提供了标签:association

方案一:封装查询结果

C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.javaStudent.java

public class Classes {//班级实体类
	private Integer id;
	private String name;
}
public class Student {//学生实体类
	private Integer id;
	private String name;
	private Integer age;
	private String sex;
	private String phone;
	private Classes classes;//学生对应的班级
}

S t u d e n t M a p p e r . j a v a StudentMapper.java StudentMapper.java

public interface StudentMapper {
	public List<Student> selectAll();
}

S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml

<mapper namespace="com.zw.mapper.StudentMapper">
	<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
		<id column="id" property="id" />
		<result column="name" property="name" />
		<result column="age" property="age" />
		<result column="sex" property="sex" />
		<result column="phone" property="phone" />
		<!-- 多对一 
			association : 表示多对一
				property : 所属类中某个对应的属性
				javaType :属性的类型
		-->
		<association property="classes" javaType="com.zw.pojo.Classes">
			<result column="class_id" property="id" />
			<result column="className" property="name" />
		</association>
	</resultMap>
	
	<select id="selectAll" resultMap="BASE_RESULT_MAP">
		SELECT 
        	st.id,st.name,st.age,st.sex,st.phone,st.class_id ,cs.name as className 
         FROM
			student st
		LEFT JOIN
        	classes cs 
        ON
        	st.class_id = cs.id
	</select>
</mapper>

T e s t . c l a s s 测 试 类 Test.class测试类 Test.class

public class Test {

	public static void main(String[] args) {
		SqlSession sqlSession = SqlSessionUtil.getSqlSession();
		StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
		List<Student> students = studentMapper.selectAll();
		for (int i = 0; i < students.size(); i++) {
			// 获取此学生拥有的班级ID
			System.out.println(students.get(i));
		}
		sqlSession.close();
	}
}
方式二:使用二次查询

C l a s s e s . j a v a 和 S t u d e n t . j a v a Classes.java 和 Student.java Classes.javaStudent.java

public class Classes {//班级实体类
	private Integer id;
	private String name;
}
public class Student {//学生实体类
	private Integer id;
	private String name;
	private Integer age;
	private String sex;
	private String phone;
	private Classes classes;//学生对应的班级
}
//省略toString()方法和getters and setters

S t u d e n t M a p p e r . j a v a 和 C l a s s e s M a p p e r . j a v a StudentMapper.java 和 ClassesMapper.java StudentMapper.javaClassesMapper.java

public interface StudentMapper {
	public List<Student> selectAll();
}
public interface ClassesMapper {
	public Classes selectById(@Param("id")Integer id);//根据班级ID查询班级信息
}	

S t u d e n t M a p p e r . x m l StudentMapper.xml StudentMapper.xml

<mapper namespace="com.zw.mapper.StudentMapper">
	<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
		<id column="id" property="id" />
		<result column="name" property="name" />
		<result column="age" property="age" />
		<result column="sex" property="sex" />
		<result column="phone" property="phone" />
        <!-- 多对一 
			association : 表示多对一
    		property : 所属类中某个对应的属性
   		    javaType :属性的类型
  		-->
		<association property="classes" javaType="com.zw.pojo.Classes" select="com.zw.mapper.ClassesMapper.selectById" column="class_id"></association>
	</resultMap>
	<sql id="BASE_ALL_COLUMN">
		<!-- 数据库中student表的各个字段 -->
		id, name, age, sex, phone,class_id
	</sql>
	<!--先查询所有的学生 根据学生中班级ID 查询班级信息 -->
	<select id="selectAll" resultMap="BASE_RESULT_MAP">
		SELECT 
			<include refid="BASE_ALL_COLUMN"></include>
		FROM
			student
	</select>
</mapper>

C l a s s e s M a p p e r . x m l ClassesMapper.xml ClassesMapper.xml

<mapper namespace="com.zw.mapper.ClassesMapper">
	<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
	</resultMap>
	<!-- 根据班级ID 查询班级 -->
	<select id="selectById" resultMap="BASE_RESULT_MAP">
		SELECT 
        	id,name 
        FROM
        	classes 
        WHERE
        	id = #{id}
	</select>
</mapper>

3.9.5.一对多

mybatis的实现方式:

方式一:封装查询结果
<!-- 
   一对多
   collection : 表示一对多
   property : 表示类中属性
   javaType : 属性的类型
   ofType :属性中本身的数据类型
  -->
<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
    <id  column="id"  property="id"/>
    <result column="name" property="name"/>
    <collection property="sts" javaType="list" ofType="Student" >
        <id  column="stId"  property="id"/>
        <result column="stName" property="name"/>
        <result column="age" property="age"/>
        <result column="sex" property="sex"/>
        <result column="phone" property="phone"/>
        <result column="class_id" property="classId"/>
    </collection>
</resultMap>
<select id="selectAll" resultMap="BASE_RESULT_MAP">
    select cs.id,cs.name,st.id as stId,st.name as
    stName,st.age,st.sex,st.phone,st.class_id from classes cs LEFT JOIN
    student st on cs.id = st.class_id
</select>
public class Classes {
	public Integer id;//班级ID
	public String name;//班级名称
	public List<Student> sts;//注意 表示多的属性
}
方式二:二次查询

ClassesMapper.xml

<resultMap type="com.zw.pojo.Classes" id="BASE_RESULT_MAP">
    <id  column="id"  property="id"/>
    <result column="name" property="name"/>
    <collection property="sts" javaType="list" ofType="Student" select="com.zw.mapper.StudentMapper.selectAll6" column="id" ></collection>
</resultMap>
<select id="selectAll" resultMap="BASE_RESULT_MAP">
    select id ,name from classes
</select>

StudentMapper.xml

<resultMap type="com.zw.pojo.Student" id="BASE_RESULT_MAP">
    <id  column="id"  property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
    <result column="sex" property="sex"/>
    <result column="phone" property="phone"/>
    <result column="class_id" property="classId"/>
    <result column="className" property="className"/>
</resultMap>

<!-- 根据班级查询学生 -->
<select id="selectAll" resultMap="BASE_RESULT_MAP">
    select <include refid="BASE_ALL_COLUMN" /> from student where class_id = #{classId}
</select>

3.11.mybatis中缓存

在mybatis缓存分为2类,一类是一级缓存,一类是二级缓存。一级缓存是指使用同一个SqlSession,二级缓存是指使用同一个SqlSessionFactory.默认Mybatis是开启一级缓存。即使用同一个SqlSession,一样sql语句,只需要访问数据库一次。

一级缓存:当使用同一个SqlSession,一样的sql只会执行一次。

public static void main(String[] args) {
    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> selectAll = studentMapper.selectAll();
    System.out.println("25行"+selectAll);
    selectAll = studentMapper.selectAll();
    System.out.println("27行"+selectAll);
    selectAll = studentMapper.selectAll();
    System.out.println("29行:"+selectAll);
    sqlSession.commit();
    sqlSession.close();
}

二级缓存:SqlSessionFactory,二级缓存需要手动开启设置

  1. 在mapper映射文件中加上:

    <!-- 开启二级缓存 -->
    <cache></cache>
    
  2. 查询实体要支持序列化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值