mybatis

1   概念

      为什么要用mybatis     
     JDBC:
          SQL夹在Java代码块里,耦合度高导致硬编码内伤,维护不易且实际开发需求中sql是有变化,频繁修改的情况多见。
 
     Hibernate和JPA:
     长难复杂SQL,对于Hibernate而言处理也不容易
     内部自动生产的SQL,不容易做特殊优化。
对开发人员而言,核心sql还是需要自己优化+掌握在自己手上,简单来说,就是一句话,
 
* sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据,可以使用简单的XML或注解
用于配置和原始映射,将接口和Java的POJO映射成数据库中的记录,完成业务代码+底层数据库的媒介

2   mybatis--helloworld

         1)  导包           

         mybatis的jar包 : mybatis-3.2.8.jar

         数据库驱动   :  mysql-connector-java-5.1.37-bin.jar

         mybatis在工作的时候,为了方便观察sql。导入日志包:   log4j.jar

         log4j必须有一个配置文件才能工作。

           2)   编写mybatis的全局配置文件。指导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>
	<!--配置所有的mybatis运行时信息 -->
	<!--http://mybatis.org/dtd/mybatis-3-config.dtd 这是约束文件,作用就是约束标签该怎么写。
	 -->
	 <!--1、配置mybatis'的数据库环境,default="development"指定默认使用哪个环境 -->
	<environments default="development">
		<!--配置一个具体的环境   id="development"指定环境的唯一标识-->
		<environment id="development">
			<!--配置事务管理器  -->
			<transactionManager type="JDBC"></transactionManager>
			<!--配置数据源。使用连接池技术.mybatis内置了数据库连接池  -->
			<dataSource type="POOLED">
				<!-- 配置连接池信息 -->
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis_0829"/>
				<property name="username" value="root"/>
				<property name="password" value="123456"/>
			</dataSource>
		</environment>
		<!-- <environment id="test"></environment>
		<environment id="product"></environment> -->
	</environments>
	
	<!--注册接口的映射文件。否则接口是找不到的。  -->
	<mappers>
		<mapper resource="com/atguigu/dao/PersonDao.xml"/>
	</mappers>
</configuration>

        3)  log4j.xml的三种配置方式

                ① 第一种

                 

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
 <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
   <param name="Encoding" value="UTF-8" />
   <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n" />
   </layout>
 </appender>
 <logger name="java.sql">
   <level value="debug" />
 </logger>
 <logger name="org.apache.ibatis">
   <level value="debug" />
 </logger>
 <root>
   <level value="debug" />
   <appender-ref ref="STDOUT" />
 </root>
</log4j:configuration>
 
 

               ②第二种简洁版  

 
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 
 
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
 <appender name="log.console" class="org.apache.log4j.ConsoleAppender">
   <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%C{1}:%M) - %m%n" />
   </layout>
 </appender>
 <!-- -->
 <logger name="org.apache.ibatis">
   <level value="info" />
   <appender-ref ref="log.console" />  
 </logger>
 
 <logger name="com.atguigu">
   <level value="debug" />
   <appender-ref ref="log.console" />  
 </logger> 
 
</log4j:configuration>
 

                 ③ 第三种 .properties文件

# For JBoss: Avoid to setup Log4J outside $JBOSS_HOME/server/default/deploy/log4j.xml!
# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
#log4j.rootLogger=INFO, stdout, logfile
log4j.rootLogger=DEBUG, stdout
 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
 
#log4j.appender.logfile=org.apache.log4j.RollingFileAppender
#log4j.appender.logfile.File=system.log
#log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files.
#log4j.appender.logfile.MaxBackupIndex=3
# Pattern to output: date priority [category] - message
#log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
#log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
 

           4) 建对应的entity持久化实体

            5)建Mapper接口并定义好方法

            6) 编写这个接口对应的xml文件。这个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">
<!-- namespace:指定接口的全类名 
mapper文件:作用:指导dao接口里面的每一个方法如何工作
-->
<mapper namespace="com.atguigu.dao.PersonDao">
	<!-- public void add(Person person); -->
	<!-- 1、指定一个插入操作。id是方法名
	parameterType:指定参数类型。
	 -->
	<insert id="add" parameterType="com.atguigu.entities.Person">
		<!--#{属性名}:取出参数里面这个属性的值  -->
		INSERT INTO tbl_person(NAME,age,birth,registerTime,salary) 
		VALUES(#{name},#{age},#{birth},#{registerTime},#{salary});
	</insert>

</mapper>

               7)在全局注册这个【配置文件】(添加到mybatis的全局配置文件里边)            

<mappers>
   <mapper resource="com/atguigu/mybatis/mapper/PersonMapper.xml"/>
 </mappers>

              8)编写Junit单元测试类,执行CRUD操作

package com.atguigu.test;

import static org.junit.Assert.*;

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

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 org.junit.Test;

import com.atguigu.dao.PersonDao;
import com.atguigu.entities.Person;

public class MyBatisTest {

	@Test
	public void test() throws IOException {
		InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
		//1、根据配置文件创建出sqlSessionFactory
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//2、获取一个称为SqlSession的对象,用来执行数据的增删改查
		SqlSession openSession = null;
		try {
			openSession = sqlSessionFactory.openSession();
			//4、获取到接口
			PersonDao mapper = openSession.getMapper(PersonDao.class);
			//5、调用方法
			mapper.add(new Person(null, "张三", 18, new Date(), new Date(), 9998.98));
			//6、提交数据
			openSession.commit();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			//7、关闭资源
			openSession.close();
		}		
	}
}

     第一个case总结:

             XXXMapper.xml文件中,namespace必须是接口全路径

             XXXMapper.xml中的id必须是接口里面的方法名 ,两者一致  

             XXXMapper.xml中的parameterType类型必须和对应方法的形参一致

             XXXMapper.xml中的resultType类型必须和对应方法的返回值类型一致

             每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,
        它也是线程不安全的,定义为局部变量

             mapper接口实现动态代理对象:    

                     定义:只需要编写mapper接口的(等价于传统的dao接口),
                                Impl实现类,也即动态代理+面向接口

                               XXXdao接口-------->XXXMapper.java

                               XXXdao接口Impl实现类-------->XXXMapper.xml

3   Mapper+Annotation入门增强

           3.1  传统的dao :         Dao接口     DaoImpl
           3. 2    Mybatis也是支持注解:

                      ①  重写接口

                       ② 在接口方法上新增加注解

public interface PersonMapperAnnotation
{
 @Insert("insert into tbl_person(name,age,birth,registerTime,salary) "
    + "values(#{name},#{age},#{birth},#{registerTime},#{salary})")
 public void add(Person person);
 
 @Delete("delete from tbl_person where id=#{id}")
 public void delete(Integer id);
 
 @Update("update tbl_person set name=#{name},age=#{age},birth=#{birth},registerTime=#{registerTime},salary=#{salary} where id=#{id}")
 public void update(Person person);
 
 @Select("select * from tbl_person where id=#{id}")
 public Person getPersonById(Integer id);
 
 @Select("select * from tbl_person")
 public List<Person> getAllPerson();
}

                ③到mybatis的配置文件里面注册该接口类(含注解)         

<!-- 配置注解接口的类 -->
   <mapper class="com.atguigu.mybatis.mapper.PersonMapperAnnotation"/> 
如果没有注册进配置文件,报如下异常信息:
 
org.apache.ibatis.binding.BindingException: Type interface com.atguigu.mybatis.mapper.PersonMapperAnnotation is not known to the MapperRegistry.
 

                 ④测试,通过getMapper重新获得注解接口版的XXXMapper.class

@Test
 public void test_Add()
 {
   SqlSession sqlSession = null;
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    PersonMapperAnnotation personMapper = sqlSession.getMapper(PersonMapperAnnotation.class);
    
    Person person = new Person();
    person.setAge(42);
    person.setBirth(new Date());
    person.setName("左冷禅");
    person.setRegisterTime(new Date());
    person.setSalary(4500.05);
    
    personMapper.add(person);
    
    sqlSession.commit();
   } catch (Exception e){
    e.printStackTrace();
   }finally{
    if(sqlSession != null) sqlSession.close();
   }
 }

   3   Mybatis的全局配置文件   mybatis-config.xml    细节

                MyBatis的XML配置文件包含了影响MyBatis行为甚深的设置和属性信息

           3.1    configuration 

                  3.1.1 properties 属性

                               JDBC--->C3P0,添加外部数据库链接演示

<!--1、引入外部配置文件作为属性 
	1)properties标签体中的key=value优先解析
	2)外部配置文件的值,如果有同名,会覆盖properties标签体中
	
	resource;引用类路径下的资源
	url:引用网络下的资源  http://www.atguigu.com/dbconfig.properties
	 -->
	<properties resource="dbconfig.properties" >
		<property name="username" value="admin"/>
	</properties>

                                   ①src文件夹下面新建db.properties

jdbc.user=root
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://127.0.0.1:3306/mybatis0607?useUnicode=true&amp;characterEncoding=UTF8

                          ②修改mybatis-config.xml

<!-- 引入外部资源文件,将数据库配置设为外部链接 -->
 <properties resource="db.properties"></properties>
 
    <environment id="development">
    <!-- 使用jdbc事务管理-->
    <transactionManager type="JDBC" />
    <!-- 数据库连接池-->
    <dataSource type="POOLED">
      <property name="driver"    value="${jdbc.driverClass}" />
      <property name="url"        value="${jdbc.jdbcUrl}" />
      <property name="username"   value="${jdbc.user}" />
      <property name="password"   value="${jdbc.password}" />
    </dataSource>
   </environment>

                3.1.2 settings  全局参数定义           

<!--2、配置某些全局属性,比如是否开启缓存等  -->
	<!-- <settings>
		<setting name="cacheEnabled" value="true"/>
	</settings> -->

7119097f4af208f8e4c6a9926f0da1fb671.jpg

b5b1835898d4d00d909a0ba089d5f874a2f.jpg

4fc71b624b39670f0468c71b4c773edcc0a.jpg

192b8f39c27e599c8e290273e8d6946aae0.jpg

     3.1.3 typeAliases 别名处理器

               别名是为 Java类型命名一个短的名字。它只和 XML 配置有关,只用来减少类名重复的部分。
          配置了mybatis-config.xml会影响XXXMapper.xml的使用

<!--3、别名处理器  -->
	<typeAliases>
		<!--为某个全类名起别名
		type:全类名
		alias:别名
		 -->
		<!-- <typeAlias type="com.atguigu.entities.Person" alias="Person"/> -->
		<!--批量别名,name:指定的是包名。这个包下的所有类,都会有别名,别名默认就是类名  -->
		<package name="com.atguigu.entities"/>
		<!--mybatis为java常用类型已经起好别名了。直接用,不区分大小写。  -->
	</typeAliases>

               默认:  

e2a305f59263a1fb40eb40e1e7be1d151fd.jpg

622db0bc3057ff59a9e8f5e387e8959db50.jpg

           自定义

               ①单个

 <typeAliases>
   <!-- 单独为Perosn进行别名设置 -->
   <typeAlias type="com.atguigu.mybatis.entities.Person" alias="Person"/>
 </typeAliases>
 
 <insert id="add" parameterType="Person">  红色部分,前面的包名已经没有了。
  insert into tbl_person(name,salary,birth,registerTime) 
      values(#{name},#{salary},#{birth},#{registerTime});
 </insert>
 

              ②多个

 <typeAliases>
   <!-- 单独为Perosn进行别名设置 
   <typeAlias type="com.atguigu.mybatis.entities.Person" alias="Person"/>-->
   
   <!--批处理别名,扫描整个包下的类 -->
   <package name="com.atguigu.mybatis.entities"/>
 </typeAliases> 
 
<insert id="add" parameterType="Person"> 红色部分,前面的包名已经没有了。
 insert into tbl_person(name,salary,birth,registerTime) 
     values(#{name},#{salary},#{birth},#{registerTime});
 </insert>

          3.1.4  typeHandlers 类型处理器

<!-- 
	4、mybatis将数据库字段映射成javaBean中某个属性的时候,默认使用对应的类型处理器在工作。
	1)、我们也可以自定义类型处理器
	2)、定义好以后注册这个类型处理
	3)、我们一般都不需要再去写这些类型处理器了
	<typeHandlers>
		<typeHandler handler=""/>
	</typeHandlers> -->
	

                         ①用于java类型和jdbc类型映射

fb83113c64e046f0cb73afa1fe9a9f67b43.jpg

②Mybatis的映射基本已经满足,不太需要重新定义

        3.1.5 environments 环境

               ①environment 环境变量

                ②transactionManager 事务管理器

                ③dataSource 数据源 

<!--5)、mybatis运行的数据库等环境的配置。  -->
	<!-- default="default":指定使用哪个环境作为默认环境 -->
	<environments default="default">
		<!--每个 environment表示一个具体的环境 -->
		<environment id="default">
			<!-- 事务管理器:
			type="JDBC":这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。 
			type="MANAGED ":这个配置几乎没做什么。它从来不提交或回滚一个连接,
					而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。
			type="自己的事务管理的全类名",如果自己写必须实现TransactionFactory接口
			 -->
			<transactionManager type="JDBC"></transactionManager>
			
			<!-- 数据源的配置
			type="UNPOOLED":不使用连接池技术。
			type="POOLED":使用连接池技术。mybatis自带的连接池
			type="JNDI":Java Named Directory Interface(java命名目录接口,ejb)
			type="指定自己的连接池的全类名":必须实现DataSourceFactory接口,返回第三方数据源即可
			 -->
			<dataSource type="POOLED">
				<!-- ${driver}取出properties配置的值 -->
				<property name="driver" value="${driver}"/>
				<property name="username" value="${username}"/>
				<property name="password" value="${password}"/>
				<property name="url" value="${url}"/>
			</dataSource>
		</environment>
	</environments>

           3.1.6  mappers 映射器 

                    1) resource:XXXMapper.xml文件

                           <mapper resource="com/atguigu/mybatis/mapper/PersonMapper.xml"/>

                     2) class:接口注解实现

                             <mapper class="com.atguigu.mybatis.mapper.PersonMapper"/>

                       3)url:外部路径  

                                ①<mapper url="file:///D:\43\mybatis_01\config\sqlmap\Person.xml" />

                                 ②不建议使用

                       4) package   

 <mappers>
   <!-- XML配置文件 -->
   <mapper resource="com/atguigu/mybatis/mapper/PersonMapper.xml"/>
   
   <!-- Annotation -->
   <mapper class="com.atguigu.mybatis.mapper.PersonMapper"/>
   
   <!-- 注册mapper包下的所有mapper接口,此种方法要求mapper接口名称和mapper映射文件名称遵守mabatis规范,且放在同一个目录中 -->
   <package name="com.atguigu.mybatis.mapper"/>
   
 </mappers>

 

<!-- 6)、注册mapper配置文件 -->
	<mappers>
		<!--resource:指定xml文件位置  -->
		<mapper resource="mybatis/mapper/PersonMapper.xml"/>
		<!--class:指定注解版的java文件位置  -->
		<mapper class="com.atguigu.dao.PersonMapperAnnotation"/>
		<mapper resource="mybatis/mapper/CatMapper.xml"/>
		<mapper resource="mybatis/mapper/DogMapper.xml"/>
		<!-- 批量注册。特别注意;必须满足一下条件 
		1)、mapper文件和mapper接口在同包下
		2)、两个同名
		-->
		<!-- <package name="com.atguigu.dao"/> -->
	</mappers>
	<!--注意点:标签是有顺序的;顺序必须正确:
	properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,plugins?,environments?,databaseIdProvider?,mappers?
	  -->

        4    SQL映射的XML文件XXXMapper.xml

                       4.1   insert、delete、update、select

                                            1)主键的生成方式 (Person插入数据后想获得主键)

                                                  ①有自增Mysql

<insert id="add" parameterType="Person" useGeneratedKeys="true" keyProperty="id">
   insert into tbl_person(name,salary,birth,registerTime) 
       values(#{name},#{salary},#{birth},#{registerTime});
 </insert>
 
useGeneratedKeys="true" keyProperty="id"

                                             ② 无自增Oracle 

 <insert id="add" parameterType="Person">
   <selectKey order="BEFORE" keyProperty="id" resultType="_int">
      select crm_seq.nextval from dual
  </selectKey>
  insert into tbl_person(name,salary,birth,registerTime) 
      values(#{name},#{salary},#{birth},#{registerTime});
 </insert>

                            2)如果需要额外功能请自行文档

                4.2  parameterType(输入参数类型)

                   1)  传入简单类型,比如按照id查Person(前面已讲过)

                    2) 传入POJO类型,前台选了多个条件

package com.atguigu.mybatis.mapper;


import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Param;

import com.atguigu.mybatis.entities.Customer;

public interface CustomerMapper {
	
	void saveCustomer(Customer customer);
	
	Customer getCustomerById(Integer custId);
	
	/**
	 * 将custName和custAge对应的查询条件封装到Customer对象中
	 * @param customer
	 * @return
	 */
	List<Customer> getCustomerByCondition01(Customer customer);
	
	/**
	 * 查询条件已经无法使用一个实体类对象来封装,所以使用Map来保存
	 * @param map
	 * @return
	 */
	List<Customer> getCustomerByCondition02(Map<String, Object> map);
	
	/**
	 * 传入多个参数
	 * @param minAge
	 * @param maxAge
	 * @return
	 */
	List<Customer> getCustomerByCondition03(
			@Param("minAgeParam") int minAge, 
			@Param("maxAgeParam") int maxAge);
	
	/**
	 * 使用Map类型封装查询结果
	 * @param orderId
	 * @return
	 */
	Map<String, Object> getDataMap(Integer orderId);
	
	/**
	 * 将每一条查询结果的Map封装到List中返回
	 * @param orderAmount
	 * @return
	 */
	List<Map<String,Object>> getDataListMap(Integer orderAmount);
	
	/**
	 * 通过resultMap的方式建立字段名和属性名之间的映射关系
	 * @param custId
	 * @return
	 */
	Customer getCustomerByResultMap(Integer custId);
	
	void createTable(@Param("tableName") String tableName);
}
<?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.atguigu.mybatis.mapper.CustomerMapper">
	
	<!-- 配置保存Customer对象的insert语句 -->
	<!-- id属性:void saveCustomer(Customer customer)方法的方法名 -->
	<!-- #{参数名}格式:如果传入参数是一个实体类对象时,参数名可以使用对象的属性名 -->
	<!-- #{}最终会被替换为?占位符,所以#{}和${}有本质区别,${}只是拼字符串不会转换成?占位符,所以有SQL注入的风险 -->
	<insert id="saveCustomer" parameterType="Customer">
		insert into tbl_cust(cust_name,cust_age) values(#{custName},#{custAge})
	</insert>
	
	<!-- 以字段别名的方式解决字段名和对象属性名不一致问题 -->
	<select id="getCustomerById" resultType="Customer">
		SELECT cust_id custId,cust_name custName,cust_age custAge 
		FROM tbl_cust 
		WHERE cust_id=#{custId}
	</select>
	
	<select id="getCustomerByCondition01" resultType="Customer">
		SELECT cust_id custId,cust_name custName,cust_age custAge 
		FROM tbl_cust 
		WHERE cust_name like #{custName} and cust_age>#{custAge}
	</select>
	
	<!-- #{key},根据key从map中获取对应的值作为SQL语句的参数 -->
	<select id="getCustomerByCondition02" resultType="Customer" parameterType="map">
		SELECT DISTINCT c.cust_id custId,cust_name custName,cust_age custAge
		FROM tbl_cust c LEFT JOIN tbl_order o ON c.`cust_id`=o.`cust_id`
		WHERE cust_name LIKE #{custNameKey} AND order_amount>#{orderAmountKey}
	</select>
	
	<select id="getCustomerByCondition03" resultType="Customer">
		SELECT cust_id custId,cust_name custName,cust_age custAge 
		FROM tbl_cust 
		<![CDATA[WHERE cust_age > #{minAgeParam} and cust_age < #{maxAgeParam}]]>
	</select>
	
	<select id="getDataMap" resultType="map">
		SELECT DISTINCT cust_name custName,order_name orderName
		FROM tbl_cust c LEFT JOIN tbl_order o ON c.`cust_id`=o.`cust_id`
		WHERE order_id=#{orderId}
	</select>
	
	<select id="getDataListMap" resultType="map">
		SELECT DISTINCT cust_name custName,order_name orderName
		FROM tbl_cust c LEFT JOIN tbl_order o ON c.`cust_id`=o.`cust_id`
		WHERE order_amount>#{orderAmount}
	</select>
	
	<!-- type属性:指定实体类类型 -->
	<resultMap type="Customer" id="customerResultMap">
		<!-- column属性:指定字段名 -->
		<!-- property属性:指定属性名 -->
		<id column="cust_id" property="custId"/>
		<result column="cust_name" property="custName"/>
		<result column="cust_age" property="custAge"/>
	</resultMap>
	
	<select id="getCustomerByResultMap" resultMap="customerResultMap">
		SELECT cust_id,cust_name,cust_age 
		FROM tbl_cust 
		WHERE cust_id=#{custId}
	</select>
	
	<update id="createTable">
		CREATE TABLE ${tableName}(
			app_id INT AUTO_INCREMENT PRIMARY KEY,
			app_name CHAR(100)
		)
	</update>
	
</mapper>

                      ①新建表tbl_cat 

CREATE TABLE `tbl_cat` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `catName` varchar(20) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `birth` date DEFAULT NULL,
  PRIMARY KEY (`id`)
);
 
INSERT INTO tbl_cat(catName,age,birth) VALUES('whiteCat',7,CURRENT_DATE());
INSERT INTO tbl_cat(catName,age,birth) VALUES('blackCat',5,CURRENT_DATE());
INSERT INTO tbl_cat(catName,age,birth) VALUES('yellowCat',3,CURRENT_DATE());

                 ② 新建Cat.java

package com.atguigu.mybatis.entities;
import java.util.Date; 
public class Cat
{
 private Integer id;
 private String  catName;
 private int     age;
 private Date    birth;
 
 public Cat() {}
 
 public Integer getId()
 {
   return id;
 }
 
 public void setId(Integer id)
 {
   this.id = id;
 }
 
 public String getCatName()
 {
   return catName;
 }
 
 public void setCatName(String catName)
 {
   this.catName = catName;
 }
 
 public int getAge()
 {
   return age;
 }
 
 public void setAge(int age)
 {
   this.age = age;
 }
 
 public Date getBirth()
 {
   return birth;
 }
 
 public void setBirth(Date birth)
 {
   this.birth = birth;
 } 
 @Override
 public String toString()
 {
   return "Cat [id=" + id + ", catName=" + catName + ", age=" + age
      + ", birth=" + birth + "]";
 }
}
 

      ③新建CatMapper接口+方法public Cat getCatByConditions(Cat cat)

package com.atguigu.mybatis.mapper;
 
import java.util.Date;
 
import com.atguigu.mybatis.entities.Cat;
 
public interface CatMapper
{
 public Cat getCatByConditions(Cat cat);
 
}
 

          ④新建CatMapper.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.atguigu.mybatis.mapper.CatMapper">
 
 <select id="getCatByConditions" parameterType="Cat" resultType="Cat">
   select * from tbl_cat where id=#{id} and age=#{age} and birth=#{birth}
 </select>
 
</mapper>

<!-- 配置保存Customer对象的insert语句 -->
	<!-- id属性:void saveCustomer(Customer customer)方法的方法名 -->
	<!-- #{参数名}格式:如果传入参数是一个实体类对象时,参数名可以使用对象的属性名 -->
	<!-- #{}最终会被替换为?占位符,所以#{}和${}有本质区别,${}只是拼字符串不会转换成?占位符,所以有SQL注入的风险 -->

           ⑤通配注册或单独注册进mybatis配置文件,否则报:is not known to the MapperRegistry

           ⑥新建单元测试类

           ⑦ 演示下like并讲解 

  •                   接口新建方法getCatByCatName
  •                            #{ } 
 
接口定义
public List<Cat> getCatByCatName(String catName);
 
Mapper.xml定义
<select id="getCatByCatName" parameterType="Cat" resultType="Cat">
 SELECT * FROM tbl_cat WHERE catName LIKE #{catName}
</select>
 
如果使用#{},方法里加入%
List<Cat> result = catMapper.getCatByCatName("%cat%");
 
推荐用它,比较安全
  •          ${ }
  •          建议+结论

 Mybatis3 防止SQL注入
#{xxx},使用的是PreparedStatement,会有类型转换,所以比较安全;推荐使用#
 
${xxx},使用字符串拼接,可以SQL注入;
 
like查询不小心会有漏动,正确写法如下:
 
Mysql: select * from t_user where name like concat('%', #{name}, '%')    
  
Oracle: select * from t_user where name like '%' || #{name} || '%'    
  
SQLServer: select * from t_user where name like '%' + #{name} + '%'

            3) 传入HashMap 

                ①接口新增方法 

public List<Cat> getCatByMap(Map<String,Object> map);

                 ②映射文件

<select id="getCatByMap" parameterType="hashmap" resultType="Cat">
   SELECT * FROM tbl_cat WHERE catName LIKE #{catName} and id=#{id}
 </select>

                ③ 测试类 

@Test
 public void test_GetCatByMap()
 {
   SqlSession sqlSession = null;
   
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    
    CatMapper catMapper = sqlSession.getMapper(CatMapper.class);
    
    Map<String,Object> map = new HashMap<String,Object>();
    map.put("catName","%cat%");
    map.put("id","3");
    
    List<Cat> list = catMapper.getCatByMap(map);
    
    System.out.println(list.size());
    
    sqlSession.close();
   }catch (Exception e){
    e.printStackTrace();
   }finally{
    if(sqlSession != null) sqlSession.close();
   }
 } 

                  4)口袋POJO,混合型传入参数,进公司看团队技术流风格

         4.3   resultType(输出参数类型)

                 1) 输出单个POJO对象,(前面已讲过)

                  2)输出多个List<POJO>,(前面已讲过)

                   3)输出hashmap

                        ①说明:select的各个字段名称作为map的key,值写入value

                        ② 接口 :

public Map<String,Object> getCatByMap2(int id);

                          ③映射文件

<select id="getCatByMap2" parameterType="int" resultType="hashmap">
   SELECT id,catName,age,birth FROM tbl_cat WHERE id=#{id}
 </select>

                          ④测试类 

@Test
 public void test_GetCatByMap2()
 {
   SqlSession sqlSession = null;
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    CatMapper catMapper = sqlSession.getMapper(CatMapper.class);
    
    Map<String,Object> map = catMapper.getCatByMap2(1);
    Iterator<Map.Entry<String,Object>> it = map.entrySet().iterator();
    while(it.hasNext())
    {
      Map.Entry<String,Object> entry = it.next();
      System.out.println(entry.getKey()+"\t"+entry.getValue());
    }
    sqlSession.close();
   }catch (Exception e){
    e.printStackTrace();
   }finally{
    if(sqlSession != null) sqlSession.close();
   }
 }

       4.4   resultMap(结果集映射)

                 POJO里面的field和数据库表里面column一一对应的话,resultType可以指定pojo将查询结              果映射为pojo

                1)假如不一致那?新建Dog演示 

                       ①新建tbl_dog

CREATE TABLE `tbl_dog` (
 `id` int(10) NOT NULL AUTO_INCREMENT,
 `dog_Name` varchar(20) DEFAULT NULL,
 `dog_Weight` int(10) DEFAULT NULL,
 PRIMARY KEY (`id`)
);
 
INSERT INTO tbl_dog(dog_Name,dog_Weight) VALUES('ww1',10);
INSERT INTO tbl_dog(dog_Name,dog_Weight) VALUES('ww2',15);
 
SELECT * FROM tbl_dog;

                      ②新建Dog.java

package com.atguigu.mybatis.entities;
 
public class Dog
{
 private Integer id;
 private String  dogName;
 private int    dogWeight;
 
 public Dog() {}
 
 public Integer getId()
 {
   return id;
 }
 
 public void setId(Integer id)
 {
   this.id = id;
 }
 
 public String getDogName()
 {
   return dogName;
 }
 
 public void setDogName(String dogName)
 {
   this.dogName = dogName;
 }
 
 public int getDogWeight()
 {
   return dogWeight;
 }
 
 public void setDogWeight(int dogWeight)
 {
   this.dogWeight = dogWeight;
 }
 
 @Override
 public String toString()
 {
   return "Dog [id=" + id + ", dogName=" + dogName + ", dogWeight="
      + dogWeight + "]";
 }
 
}
 

                 ③新建DogMapper接口,getDogByName

public interface DogMapper
{
 public List<Dog> getDogByDogName(String dogName);
}

                    ④新建DogMapper.xml  

 <select id="getDogByDogName" parameterType="String" resultType="Dog">
   SELECT id,dog_Name,dog_Weight FROM tbl_dog WHERE dog_Name LIKE #{dogName}
 </select>

                      ⑤通配注册或者新注册到Mybatis配置文件

                      ⑥test

                             DogMapper.xml

<select id="getDogByDogName" parameterType="String" resultType="Dog">
   SELECT *  
   FROM tbl_dog WHERE dog_Name LIKE #{dogName}
 </select>

                               test code 

public class DogMapperTest
{
 
 private SqlSessionFactory sqlSessionFactory = null;
 
 @Before
 public void createSqlSessionFactory() throws IOException
 {
   String resource = "mybatis-config.xml";
   InputStream inputStream = Resources.getResourceAsStream(resource);
   sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 }
 
 
 @Test
 public void test()
 {
   SqlSession sqlSession = sqlSessionFactory.openSession();
   
   DogMapper dogMapper = sqlSession.getMapper(DogMapper.class);
   List<Dog> list = dogMapper.getDogByDogName("%dog%");
   for (Dog dog : list)
   {
    System.out.println(dog.toString());
   }
   
   sqlSession.close();
 }
 
}
 

                  2)  别名查询

<select id="getDogByDogName" parameterType="String" resultType="Dog">
   SELECT id as id, dog_Name as dogName, dog_Weight as dogWeight  
   FROM tbl_dog WHERE dog_Name LIKE #{dogName}
 </select>
 

                    3)结果映射

 <!-- 靠resultMap来解决,类名和字段名不一致的情况 -->
 <!--  id:查询结果集的唯一标识
    column:数据库字段
    property:POJO类的属性字段
    result:映射结果,表示将数据库的值对应映射给POJO的类属性上
  -->
 <resultMap id="dogResultMap" type="Dog" >
   <id column="id" property="id"/>
   <result column="dog_Name" property="dogName"/>
   <result column="dog_Weight" property="dogWeight"/>
 </resultMap>
 <select id="getDogByDogName2" parameterType="String" resultMap="dogResultMap">
   SELECT id,dog_Name,dog_Weight  FROM tbl_dog WHERE dog_Name LIKE #{dogName}
 </select>

            4.5 sql(定义重用的sql)

sql
这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化. 比如: 
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
这个 SQL 片段可以被包含在其他语句中,例如: 
<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>
属性值可以用于包含的refid属性或者包含的字句里面的属性值,例如: 
<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"/>
    <property name="include_target" value="sometable"/>
  </include>
</select>
 

         5   动态sql

                 5.1 概述

                 5.2  新建Student演示     

                 5.3 if  

                              1)  接口方法

public List<Student> getStudent_If(int age);

                               2)传入单参数age返回List<Student>,有就拼接没有就不参与,按照年龄查学生

                              3)重点难点:单参数时,mybatis找不到参数错误现象和处理

 
单独传入一个值,在开发中经常看到如下错误:
org.apache.ibatis.exceptions.PersistenceException: 
 
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'age' in 'class java.lang.Integer'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'age' in 'class java.lang.Integer'
 
 
三种方法处理:
 
1 接口方法中添加注解@Param
 之前:public List<Student> getStudentByIf(int age);
 之后:public List<Student> getStudentByIf(@Param("age") int age);
 
2 XXXMapper.xml配置文件里面写_parameter
 <select id="getStudentByIf" parameterType="int" resultType="Student">
 select * from tbl_student where 1 = 1
   <if test="_parameter > 0">
 and age = #{age}
   </if>
 </select>
 
3 直接传递对象

              5.4 where

                          1) 接口方法

public interface StudentMapper
{
public List<Student> getStudent_If(int age);
 public List<Student> getStudent_Where(Student student);
 
}

                           2)Mapper文件

<mapper namespace="com.atguigu.mybatis.mapper.StudentMapper">
 
 <select id="getStudentById" parameterType="int" resultType="Student">
  select * from tbl_student where id=#{id}
 </select>
 
 <!-- where/if -->
 <select id="getStudentByCondition" parameterType="Student" resultType="Student">
  select * from tbl_student
   <where>
    <if test=" score !=null &amp;&amp; score != '' ">
     score > #{score}
    </if>
       <!--使用name !=null and name != ''也可以-->
    <if test=" name !=null &amp;&amp; name != '' ">
     and name like #{name}
    </if>    
   </where>
 </select>
   
</mapper>

                          3)where子句以and 或者or开头,它会自动删除第一个and或者or

                          4)一般建议Where和if结合起来用

                5.5 choose/when/otherwise

                         1)接口方法

public List<Student> getStudent_choosewhenotherwise(Map<String,Object> map);

                          2)Mapper文件

<select id="getStudentByOption" parameterType="hashmap" resultType="Student">
   select * from tbl_student
   <choose>
    <when test=" id != null &amp;&amp; id !='' ">
      where id >= #{id}
    </when>
    <when test=" birth != null &amp;&amp; birth !='' ">
      where birth > #{birth}
    </when>
    <otherwise>
      where 1 = 1
    </otherwise>
   </choose>
 </select>

               5.6 foreach

                           1) SQL里面的IN查询

                           2) if+where+foreach

                              ①接口 

public List<Student> getStudent_foreach(Map<String,Object> map);

                              ② 映射文件

<select id="getStudentByForEach" parameterType="Map" resultType="Student">
   select * from tbl_student
   <if test="ages != null &amp;&amp; ages !='' ">
    <where>
      age in
      <foreach item="ages" collection="ages" open="(" separator="," close=")">
       #{ages}
      </foreach>
    </where>
   </if>
 </select>

                                ③test

@Test
 public void test_ForEach()
 {
   SqlSession sqlSession = null;
   Map<String,Object> map = new HashMap<String,Object>();
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    
    List<Integer> ages = new ArrayList<Integer>();
    ages.add(33);
    ages.add(64);
    ages.add(0);
    
    map.put("ages",ages);
    
    List<Student> list = studentMapper.getStudentByForEach(map);
    for (Student element : list)
    {
      System.out.println(element.toString());
    }
   } catch (Exception e){
    e.printStackTrace();
   }finally{
    if(sqlSession != null)sqlSession.close();
   }
 }

            5.7 trim

                        1) 接口 

public List<Student> getStudentByTrim(Student student);
或者
public List<Student> getStudentByTrim(Map<String,Object> map);

                        2)映射文件

<select id="getStudentByTrim" parameterType="Student" resultType="Student">
   select * from tbl_student
   <trim prefix="where" prefixOverrides="and|or">
    <if test="age > 0">
      and age = #{age}
    </if>
    <if test="name !=null and name !='' ">
      and name like #{name}
    </if>    
   </trim>
 </select>

                        3)trim+if,能替代where

                        4)前后缀动态覆盖拼装insert

 
public void insertByTrim(Map<String,Object> map);
 
<insert id="insertByTrim" parameterType="map">
   insert into tbl_student
   <trim prefix="(" suffix=")" suffixOverrides=",">
    <if test="name != null">
      name,
    </if>
    <if test="score != null">
      score,
    </if>
    <if test="birth != null">
      birth,
    </if>
    <if test="age != null">
      age,
    </if>                 
   </trim>
   <trim prefix=" values (" suffix=")" suffixOverrides=",">
    <if test="name != null">
      #{name},
    </if>
    <if test="score != null">
      #{score},
    </if>
    <if test="birth != null">
      #{birth},
    </if>
    <if test="age != null">
      #{age},
    </if>    
   </trim>
 </insert>

           5.8   set

                   1)动态包含需要更新的列set+if

                              ①会动态前置set关键字

                              ②自动删除最后一个英文逗号

                   2)自动删除最后一个英文逗号

                               ①接口

public void updateStudent(Student student);

                             ②映射文件

<update id="updateStudent" parameterType="Student">
   <!-- 
   update tbl_student 
   set name=#{name},score=#{score},birth=#{birth},age=#{age} 
   where id=#{id} 
   -->
   update tbl_student
   <set>
    <if test="name !=null and name !=''">name=#{name},</if>
    <if test="score >0 ">score=#{score},</if>
    <if test="birth !=null and birth !=''">birth=#{birth},</if>
    <if test="age !=null and age !=''">age=#{age},</if>
   </set>
   where id=#{id}    
 </update>

       6    关联查询

              6.1 通用步骤

                        1)新建数据表+新增内容数据

                        2)建entity

                        3)Mapper接口

                        4)对应接口的Mapper.xml

                        5)通配注册或在mybatis-config.xml里面新增映射配置

                         6)测试类

               6.2   一对一

                       1)  配置版

                            ①新建数据表+新增数据表内容

CREATE TABLE `tbl_key` (
  `id` INT(10) NOT NULL AUTO_INCREMENT,
  `keyName` VARCHAR(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
);
 
CREATE TABLE `tbl_lock` (
  `id` INT(10) NOT NULL AUTO_INCREMENT,
  `lockName` VARCHAR(20) DEFAULT NULL,
  `key_id` INT(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_tblKey_id` (`key_id`),
  CONSTRAINT `fk_tblKey_id` FOREIGN KEY (`key_id`) REFERENCES `tbl_key` (`id`)
) 
 
SELECT * FROM tbl_key,tbl_lock WHERE tbl_key.id = tbl_lock.key_id AND tbl_lock.id=1
 
 
SELECT * FROM tbl_key LEFT OUTER JOIN tbl_lock ON tbl_key.id = tbl_lock.key_id WHERE tbl_lock.id=1

                          ②建entity

public class Key
{
 private Integer id;
 private String  keyName;
 
}
 
public class Lock
{
 private Integer id;
 private String  lockName;
 private Key     key;
}
 

                        ③ Mapper定义接口

                               KeyMapper.java

public interface KeyMapper
{
 public Key getKeyById(Integer id);
}

                               LockMapper.java

public interface LockMapper
{
 public Lock getLockById(Integer id);
}

                          ④KeyMapper.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.atguigu.mybatis.mapper.KeyMapper">
 
 <select id="getKeyById" parameterType="Integer" resultType="Key">
   select * from tbl_key where id=#{id}
 </select>
 
</mapper>

                            ⑤LockMapper.xml实现,第一种(ResultMap+连接查询)

<?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">
<!-- 
  association:用于1:1关联查询
  javaType:对象属性的完整类型,建议包名+类名
  property:entity对象的属性名称
  column:对应数据库表的字段
 -->
<mapper namespace="com.atguigu.mybatis.mapper.LockMapper">
 <resultMap id="LockResultMap" type="Lock">
   <id property="id" column="id"/>
   <result property="lockName" column="lockName"/>
 <association property="key" javaType="com.atguigu.mybatis.entities.Key">
  <id property="id" column="id"/>
  <result property="keyName" column="keyName"/>
 </association>
 </resultMap> 
 <select id="getLockById" parameterType="Integer" resultMap="LockResultMap" >
 SELECT * FROM tbl_lock ,tbl_key 
 WHERE tbl_lock.key_id = tbl_key.id 
 AND tbl_lock.id = #{id}
 </select>
</mapper>

                           ⑥LockMapper.xml实现,第二种(ResultMap+select分段查询)

<?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.atguigu.mybatis.mapper.LockMapper">
 
<resultMap type="Lock" id="LockResultMap">
   <id property="id" column="id"/>
   <result property="lockName" column="lockName"/>
   <association property="key" column="key_id"  select="com.atguigu.mybatis.mapper.KeyMapper.getKeyById"/>
 </resultMap> 

 <select id="getLockById" parameterType="Integer" resultMap="LockResultMap" >
  SELECT * FROM tbl_lock where id=#{id}
 </select>
</mapper>
 
lock外键是key表的主键
<association property="key" column="key_id"  select="com.atguigu.mybatis.mapper.KeyMapper.getKeyById"/>

                             ⑦延时加载介绍4da195a37ae4dc51774d026895aaa13d919.jpg

                           2)注解版

                    ①KeyMapperAnnotation

public interface KeyMapperAnnotation
{
 @Select("select * from tbl_key where id=#{id}")
 public Key getKeyById(Integer id);
}

                         ②LockMapperAnnotaion

public interface LockMapperAnnotaion
{
 @Select("select * from tbl_lock where id=#{id}")
 @Results({
   @Result(id=true,property="id",column="id"),
   @Result(property="lockName",column="lockName"),
   @Result(property="key",column="key_id",
one=@One(select="com.atguigu.mybatis.mapper.KeyMapperAnnotation.getKeyById"))
 })
 public Lock getLockById(Integer id);
}

             6.3   一对多

                          配置版

                     1)先是单向1:N,一个部门下有多个员工

                           ①建entity

                                  Dept.java

import java.util.List;
 
public class Dept 
{
 private Integer id;
 private String  deptName;
 private String  locAdd;
 private List<Emp> emps;
 
 
 public Dept() {}
 
 
 public Integer getId() {
   return id;
 }
 
 
 public void setId(Integer id) {
   this.id = id;
 }
 
 
 public String getDeptName() {
   return deptName;
 }
 
 
 public void setDeptName(String deptName) {
   this.deptName = deptName;
 }
 
 
 public String getLocAdd() {
   return locAdd;
 }
 
 
 public void setLocAdd(String locAdd) {
   this.locAdd = locAdd;
 }
 
 
 public List<Emp> getEmps() {
   return emps;
 }
 
 
 public void setEmps(List<Emp> emps) {
   this.emps = emps;
 }
 
 
 @Override
 public String toString()
 {
   return "Dept [id=" + id + ", deptName=" + deptName + ", locAdd="
      + locAdd + ", emps=" + emps + "]";
 }
 
 
 
}
 

                          Emp.java

public class Emp 
{
 private Integer id;
 private String  name;
 //private Dept    dept;//后续双向关系的时候记得添加
 
 public Emp() {}
 
 public Integer getId() {
   return id;
 }
 public void setId(Integer id) {
   this.id = id;
 }
 public String getName() {
   return name;
 }
 public void setName(String name) {
   this.name = name;
 }
 public Dept getDept() {
   return dept;
 }
 public void setDept(Dept dept) {
   this.dept = dept;
 }
 
 @Override
 public String toString() {
   return "Emp [id=" + id + ", name=" + name + ", dept=" + dept + "]";
 }
 
 
 
 
}

                    ②新建DeptMapper接口和方法,按照id查询部门

import com.atguigu.mybatis.entities.Dept;
 
public interface DeptMapper
{
 public Dept getDeptById(Integer id);
}

                    ③新建DeptMapper.xml

<mapper namespace="com.atguigu.mybatis.mapper.DeptMapper">
 <resultMap id="DeptResultMap" type="Dept">
   <id property="id" column="id"/>
   <result property="deptName" column="deptName"/>
   <result property="locAdd" column="locAdd"/>
   <!-- private List<Emp> emps; column="id"写被集合对象主键,select按照外键键查询,通过deptid查出emp给dept-->   
   <collection property="emps" column="id" ofType="Emp" select="com.atguigu.mybatis.mapper.EmpMapper.getEmpByDeptId"/>
 </resultMap>
 <select id="getDeptById" parameterType="Integer" resultMap="DeptResultMap">
        select * from tbl_dept where id=#{id}
 </select>
</mapper>
=====================================================================================
解释如下:
collection :一对多关联查询
ofType:集合中元素对象的类型

                    ④新建EmpMapper接口和方法

public interface EmpMapper
{
 public List<Emp> getEmpByDeptId(Integer deptId); 
}

                      ⑤新建EmpMapper.xml

<mapper namespace="com.atguigu.mybatis.mapper.EmpMapper">
 
 <resultMap  id="EmpResultMap" type="Emp">
   <id property="id" column="id"/>
   <result property="name" column="name"/>
 </resultMap>
 <select id="getEmpByDeptId" parameterType="Integer" resultMap="EmpResultMap">
   select * from tbl_emp where deptId=#{deptId}
 </select>
</mapper>

                       ⑥测试junit,查出一个部门下所有员工

public class TestOne2Many
{
 private static Logger logger = Logger.getLogger(TestOne2Many.class);
 private SqlSessionFactory sqlSessionFactory = null;
 
 @Before
 public void init() throws IOException
 {
   String resource = "mybatis-config.xml";
   InputStream inputStream = Resources.getResourceAsStream(resource);
   sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 }
 
 
 @Test
 public void test_GetDeptById()
 {
   SqlSession sqlSession = null;
   
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = deptMapper.getDeptById(1);
    
    System.out.println("############"+dept.getDeptName());
    List<Emp> list = dept.getEmps();
    for (Emp emp : list)
    {
      System.out.println(emp.toString());
    }
    
   } catch (Exception e){
    e.printStackTrace();
   }finally{
    if(null != sqlSession) sqlSession.close();
   }
 }
 
}

               2)再来双向1:N,每一个员工都属于一个部门

                    ①Emp类里新增定义 private Dept dept;

                    ②接口EmpMapper新增方法getEmpById

public interface EmpMapper
{
 //为部门查询服务,从一的一端查询,协助按照deptId查询并获得所有员工
 public List<Emp> getEmpByDeptId(Integer deptId); 
 //为员工查询服务,查出该员工信息+属于哪个部门
 public Emp getEmpById(Integer id);
}

                    ③修改EmpMapper.xml

<mapper namespace="com.atguigu.mybatis.mapper.EmpMapper">
 
 <resultMap  id="EmpResultMap" type="Emp">
   <id property="id" column="id"/>
   <result property="name" column="name"/>
   <association property="dept" column="deptId" select="com.atguigu.mybatis.mapper.DeptMapper.getDeptById"/>   <!--双向映射,员工里面有部门 -->
 </resultMap>
 <!-- 为部门查询服务,从一的一端查询,协助按照部门id查询并获得所有员工 -->
 <select id="getEmpByDeptId" parameterType="Integer" resultMap="EmpResultMap">
 select * from tbl_emp where deptId=#{deptId}
 </select>
 <!-- 为员工查询服务,查出该员工自身信息+属于哪个部门 -->
 <select id="getEmpById" parameterType="Integer" resultMap="EmpResultMap">
 select * from tbl_emp where id=#{id}
 </select> 
 
</mapper>

                        ④测试junit,查出某员工属于哪个部门

@Test
 public void test_GetEmpById()
 {
   SqlSession sqlSession = null;
   
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    
    EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
    Emp emp = empMapper.getEmpById(1);
    
    System.out.println(emp.toString());
    
   } catch (Exception e){
    e.printStackTrace();
   }finally{
    if(null != sqlSession) sqlSession.close();
   }
 }

                       注解版

                    3)先是单向1:N,一个部门下有多个员工

                          ①新建接口DeptMapperAnnotation+添加注解

public interface DeptMapperAnnotation
{
 @Select("select * from tbl_dept where id=#{id}")
 @Results({
   @Result(id=true,property="id",column="id"),
   @Result(property="deptName",column="deptName"),
   @Result(property="locAdd",column="locAdd"),
   @Result(property="emps",column="id",
many=@Many(select="com.atguigu.mybatis.mapper.EmpMapperAnnotation.getEmpByDeptId"))
 })
 public Dept getDeptById(Integer id);
}

                       ②新建接口EmpMapperAnnotation+添加注解

public interface EmpMapperAnnotation
{
 @Select("select * from tbl_emp where deptId=#{deptId}") 
 @Results({
   @Result(id=true,property="id",column="id"),
   @Result(property="name",column="name")
 })
 public List<Emp> getEmpByDeptId(Integer deptId); 
}

                            ③通配注册或新增注册进mybatis配置文件

                            ④测试1:N

             4)再来双向1:N,每一个员工都属于一个部门

                   ①修改EmpMapperAnnotation接口

 @Select("select * from tbl_emp where id=#{id}")
 @Results({
   @Result(id=true,property="id",column="id"),
   @Result(property="name",column="name"),
   @Result(property="dept",column="deptId",
one=@One(select="com.atguigu.mybatis.mapper.DeptMapperAnnotation.getDeptById"))
 })
 public Emp getEmpById(Integer id);

                         ②测试

@Test
 public void test_GetEmpByIdAnnotation()
 {
   SqlSession sqlSession = null;
   
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    
    EmpMapperAnnotation empMapperAnnotation = sqlSession.getMapper(EmpMapperAnnotation.class);
    Emp emp = empMapperAnnotation.getEmpById(1);
    
    System.out.println(emp.toString());
    
   } catch (Exception e){
    e.printStackTrace();
   }finally{
    if(null != sqlSession) sqlSession.close();
   }
 }

=============================================================

1.搭建环境

①导入jar包

[1]MyBatis本身的核心jar包:mybatis-3.2.8.jar

[2]MySQL数据库驱动:mysql-connector-java-5.1.37-bin.jar

[3]数据库连接池(可选):c3p0-0.9.1.2.jar

[4]日志抓取:log4j-1.2.17.jar

②提供连接数据库的基本信息:jdbc.properties

jdbc.user=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/mybatis160829

③创建MyBatis自身的配置文件:mybatis-config.xml

④创建log4j专用的配置文件:log4j.properties

log4j.rootLogger=DEBUG,myConsole

log4j.appender.myConsole=org.apache.log4j.ConsoleAppender

log4j.appender.myConsole.Target=System.out

log4j.appender.myConsole.layout=org.apache.log4j.PatternLayout

log4j.appender.myConsole.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n

Tips:所有我们自定义的东西,如果想要让框架找到,只有两个办法

    • 以某种方式明确告诉框架文件的路径和文件名:mybatis-config.xml
    • 遵守框架默认的约定:log4j.properties

2.给SQL语句传入参数的方式

①单一值:Integer、String、……

传入方式:Customer getCustomerById(Integer custId);

在XML中引用的方式:#{随意}

②实体类对象:Customer等

传入方式:List<Customer> getCustomerByCondition01(Customer customer)

在XML中引用的方式:#{实体类对象的属性名}

③Map

传入方式:List<Customer> getCustomerByCondition02(Map<String, Object> map)

在XML中引用的方式:#{map的key}

④@Param注解

传入方式:List<Customer> getCustomerByCondition03(

@Param("minAgeParam") int

@Param("maxAgeParam") int maxAge);

在XML中引用的方式:#{注解中指定的名称}

3.获取SQL语句执行结果数据

①封装到实体类对象中

必要的配置:resultType="Customer"

SQL语句中可以使用别名的方式解决字段名和属性名不一致问题

SELECT cust_id custId,cust_name custName,cust_age custAge ...

接口方法返回值类型:实体类类型

Customer getCustomerById(Integer custId);

②封装到实体类对象的集合中

必要的配置:resultType="Customer"

对于集合类型的数据,不必考虑集合类型,只需要指定实体类类即可

SQL语句中可以使用别名的方式解决字段名和属性名不一致问题

SELECT cust_id custId,cust_name custName,cust_age custAge ...

接口方法返回值类型:实体类类型的集合

List<Customer> getCustomerByCondition01(Customer customer)

③将每一条记录封装到Map中

必要的配置:resultType="map"

SQL语句中对是否指定别名没有要求

返回值类型:Map<String,Object>

④如果查询结果有多条记录,则返回Map的List

必要的配置:resultType="map"

SQL语句中对是否指定别名没有要求

返回值类型:List<Map<String,Object>>

⑤使用resultMap建立字段名和属性名之间的映射关系

<!-- type属性:指定实体类类型 -->

<resultMap type="Customer" id="customerResultMap">

     <!-- column属性:指定字段名 -->

     <!-- property属性:指定属性名 -->

     <id column="cust_id" property="custId"/>

     <result column="cust_name" property="custName"/>

     <result column="cust_age" property="custAge"/>

</resultMap>

<select id="getCustomerByResultMap" resultMap="customerResultMap">

     SELECT cust_id,cust_name,cust_age

     FROM tbl_cust

     WHERE cust_id=#{custId}

</select>

⑥单一值

resultType="int/double/String/..."

4.${}和#{}的区别

${}仅仅是拼字符串

使用的场合:一般是在无法使用#{}或者说不能用?占位符的地方使用,例如:创建数据库表时表名

#{}是读取数据后作为?占位符的参数数据

使用的场合:给SQL语句传入具体数值时

 

5.关联关系

①生活中,数据之间的关联关系

[1]多对一:Order和OrderItem;Customer和Order

[2]一对一:Husband和Wife;Key和Lock

[3]多对多:Teacher和Student

②MyBatis对关联关系的支持

[1]对多:获取多的一端

order.getOrderItemSet();

teacher.getStudentSet();

不必关心是从多还是一的一端出发,总之目标是获取一个集合对象

 

[2]对一:获取一的一端

orderItem.getOrder();

wife.getHusband();

不必关心是从多还是一的一端出发,总之目标是获取一个单个对象

③MyBatis中建立关联关系的配置方式

[1]对一

一次性全部加载:

简洁版:在resultMap标签内通过级联属性将数据注入

经典版:association标签配合javaType属性

分两条SQL语句加载:association标签配合select属性

 

[2]对多

一次性全部加载:collection标签配合ofType属性

分两条SQL语句加载:collection标签配合select属性

④测试关联关系的实验

[1]根据orderId查询Order对象,同时将关联的Customer对象也查询出来:使用resultMap

[2]根据orderId查询Order对象,同时将关联的Customer对象也查询出来:association配合javaType

[3]根据orderId查询Order对象,使用另外一条SQL语句查询Customer对象:association配合select

[4]根据custId查询Customer对象,同时将关联的Order对象的集合也查询出来

collection标签配合ofType属性

[5]根据custId查询Customer对象,使用另外一条SQL语句查询关联的Order对象的集合

collection标签配合select属性

⑤为什么要使用resultMap?

因为SQL语句不支持级联形式的别名fe213bebd92fb10ce9857a3df0323b1a5dc.jpg

    c.`cust_id` customer.custId,

    cust_name customer.custName,

    cust_age customer.custAge

这样写不符合SQL语法

⑥为什么分两条语句加载?

如果通过左外连接的方式用一条SQL语句进行查询,那么无法实现“延迟加载”效果。如果想要实现延迟加载效果,前提就是用一条SQL语句查询Order,另一条SQL语句查询Customer。

 

⑦小结

[1]对一

一次性加载:association+javaType

分两条SQL语句加载:association+select

[2]对多

一次性加载:collection+ofType

分两条SQL语句加载:collection+select

 

6.延迟加载/懒加载

①概念:对于一个实体类对象(Order)关联的其他对象(Customer),在查询Order时并没有马上把关联的Customer也查询出来,而是等到真正用到Customer属性时才发送SQL语句进行查询。

②影响系统性能的两个方面

[1]访问数据库的次数:越少越好

[2]内存空间:空余空间越大越好

③三种查询方式比较

[1]立即加载:一定会发送两条SQL语句

确定Customer需要用到,但是无法通过左外连接等技术手段一次性查询得到

 

[2]延迟加载:可能发送两条SQL语句,也可能只发送一条

查询Order之后,Customer可能不会被用到,甚至我们确定Customer根本就不会被用到

 

[3]左外连接:发送一条SQL语句

查询Order的同时,确定Customer一定会被使用,或有很大的可能性要使用

④MyBatis环境下使用延迟加载需要在mybatis-config.xml配置文件中加入如下配置

<settings>

     <!-- 开启延迟加载功能 -->

     <setting name="lazyLoadingEnabled" value="true"/>

     

     <!-- 关闭立即加载功能 -->

     <setting name="aggressiveLazyLoading" value="false"/>

     

</settings>

注意:还需要额外导入两个jar包

asm-3.3.1.jar

cglib-2.2.2.jar

 

7._parameter

当动态SQL标签的属性中需要引用外部传入的单一值时,单一值没有通过@Param注解明确指定名称,则使用_parameter来引用

8.缓存

①存在的意义:提升项目运行的性能。

如果查询、获取某个数据的操作是比较消耗资源的,那么我们应该在获取之后将数据在缓存中保存起来,这样下一次再使用就不必重新执行“昂贵”操作了。

②MyBatis缓存体系

[1]一级缓存:线程级别,由SqlSession对象负责维护的

[2]二级缓存:进程级别,由SqlSessionFactory对象负责维护的

内置

第三方51f405bd28c3fb558a18ddc956682a37b2e.jpg

③进程和线程的关系

[1]空间的角度

8d07dbdd9c7e7e0d0af89c25aed9a5fe7ff.jpg

[2]时间的角度

衡量进程(服务器进程)执行时间的单位:月/年

衡量线程(处理请求的线程)执行时间的单位:毫秒/秒

④★二级缓存中适合和不适合保存的数据

[1]适合

      • 经常被查询,而且很少被修改的数据
      • 不是特别重要,允许偶尔的并发问题
      • 不会被其他应用程序修改

[2]不适合

      • 经常被修改的数据
      • 特别重要,不允许任何的并发问题,例如:财务数据
      • 有可能被其他应用程序修改的数据

⑤二级缓存的缓存命中率:成功从缓存中获取到数据的次数/查询总次数

4a6f69d039f83b6520ab37de4cb92b623b6.jpg

⑥EHCache配置文件说明

<ehcache>

 <!-- 指定一个硬盘上的目录,用于在内存中的缓存区域溢出时保存对象 -->

     <diskStore path="D:\\MyCache"/>

 <!-- defaultCache:默认缓存区域的设置 -->

     <defaultCache

 <!-- 缓存区域中最多保存多少个元素 -->

          maxElementsInMemory="10000"

 <!-- 是否永不过期,如果设置为true,那么会忽略timeToIdleSeconds和timeToLiveSeconds 属性的设置-->

          eternal="false"

 <!-- 控制数据在缓存中,闲置的最长时间 -->

          timeToIdleSeconds="120"

 <!-- 控制数据在缓存中,存活的总时间 -->

          timeToLiveSeconds="120"

 <!-- 内存中的缓存空间满了以后是否要保存到硬盘上 -->

          overflowToDisk="true"

          />

</ehcache>

⑦缓存的本质:Map

//1.从Map形式的缓存区域中获取key对应的value

Object value = map.get(key);

//2.检查value是否为空

if(value == null){

//3.当前需要的数据并没有事先存入缓存,所以需要真正查询数据库得到

value = mapper.getById(5);

/4.将value存入缓存区域

map.put(key,value);

}

//5.将value返回

return value;

 

9.MyBatis和Spring整合开发

①整合的思路

e97ec14007a9a6b354aa228078a93bd61bf.jpg

 

②整合的任务

[1]引入外部属性文件:jdbc.properties

[2]配置数据源的bean

[3]配置SqlSessionFactory的bean

      • 装配数据源的bean
      • 指定MyBatis自身配置文件的位置
      • 配置typeAliasPackage

[4]配置事务管理器bean:装配数据源

[5]开启基于注解的声明式事务的功能:装配事务管理器

[6]配置XxxMapper的扫描器

③需要额外导入的jar包:mybatis-spring-1.2.2.jar

④Spring需要的jar包

[1]SpringIOC容器需要的jar包

[2]Spring声明式事务需要的jar包

=====================================================================

        7     查询缓存

                    7.1 介绍

       1 默认情况下,mybatis是启用一级缓存的,它是SqlSession级别的,也即同一个SqlSession接口对象调用了相同的select语句,
就会从缓存里面拿到,而不是再去查询一次数据库,给学生演示下看看。
 
        默认情况下,select使用缓存,增删改不使用。当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
 
          2 二级缓存
 
          3 外部缓存
 
           4 作用域
           正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持;
         一级缓存基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
 
          二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache、Hazelcast等。

                7.2  一级缓存

                       1) 案例步骤

                          ①建表tbl_user

CREATE TABLE tbl_user(
  id INT PRIMARY KEY AUTO_INCREMENT, 
  NAME VARCHAR(20), 
  age INT
);
INSERT INTO tbl_user(NAME, age) VALUES('苏武', 22);
INSERT INTO tbl_user(NAME, age) VALUES('李青', 31);
 

                          ②建entity

public class User
{
 private Integer id;
 private String  name;
 private int     age;
 
 public User(){}
 
 
 
 public User(Integer id, String name, int age)
 {
   super();
   this.id = id;
   this.name = name;
   this.age = age;
 }
 
 
 
 public Integer getId()
 {
   return id;
 }
 
 public void setId(Integer id)
 {
   this.id = id;
 }
 
 public String getName()
 {
   return name;
 }
 
 public void setName(String name)
 {
   this.name = name;
 }
 
 public int getAge()
 {
   return age;
 }
 
 public void setAge(int age)
 {
   this.age = age;
 }
 
 @Override
 public String toString()
 {
   return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
 }
 
 
}

                              ③UserMapper接口和方法

public interface UserMapper
{
 public User getUserById(Integer id);
 
 public void updateUser(User user);
}

                           ④UserMapper.xml实现

<mapper namespace="com.atguigu.mybatis.mapper.UserMapper">
 
 <select id="getUserById" parameterType="Integer" resultType="User">
   select * from tbl_user where id=#{id}
 </select>
 
 <update id="updateUser" parameterType="User">
   update tbl_user set name=#{name},age=#{age} where id=#{id}
 </update>
 
</mapper>

                          ⑤通配注册或新增注册到配置文件

                           ⑥演示缓存失效情况

@Test
 public void test_OneLevelCache()
 {
   SqlSession sqlSession = null;
   try
   {
    sqlSession = sqlSessionFactory.openSession();
    
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    User user = userMapper.getUserById(1);
    System.out.println(user.toString());
 
    /**
    * 一级缓存默认会被使用,执行下面的结果可以看到,User user2,但是sql还是一条*/
    User user2 = userMapper.getUserById(1);
    System.out.println(user2.toString());
    
    /**
    * 1必须是同样的查询条件
    User user2 = userMapper.getUserById(2);
    System.out.println(user2.toString()); */
    
    /**
    * 2必须是同一个sqlSession,如果关闭过一次,那就另外算了。
    sqlSession.close();
    sqlSession = sqlSessionFactory.openSession();
    userMapper = sqlSession.getMapper(UserMapper.class);
    User user2 = userMapper.getUserById(1);
    System.out.println(user2.toString());*/    
     
    /**
    * 3没有执行过sqlSession.clearCache(); 
    sqlSession.clearCache();
    User user2 = userMapper.getUserById(1);
    System.out.println(user2.toString());*/
    
    /**
     * 
     * 4没有执行过增删改 
    userMapper.updateUser(new User(1,"lisi",363));
    sqlSession.commit();
    user = userMapper.getUserById(1);
    System.out.println(user.toString());*/   
         
    
   } catch (Exception e){
    e.printStackTrace();
   }finally{
    if(null != sqlSession) sqlSession.close();
   }
 }

                        2) 原理结论

Mybatis内部缓存是使用了一个HashMap,map数据结构需要key/value
key:hashCode+查询的SqlId+编写的sql查询语句
value:执行上面的查询所获得java对象
 
一级缓存作用域是SqlSession,每次查询先找缓存,找到使用,
找不到再从数据库查询。查询到数据将数据写入缓存。
 
缓存启效前提条件:
1查询条件一样
2必须是同一个sqlSession,如果关闭过一次,那就另外算了。
3没有执行过sqlSession.clearCache();
4没有执行过增删改,sqlSession执行insert、update、delete等操作, commit提交后会清空缓存区域。

               7.3 二级缓存

                       1)案例步骤

                                ①mybatis-config.xml配置中开启显示的定义二级缓存

db019a36f770573d285a4599210d24b28af.jpg

                         ②提醒,用二级缓存时entity类必须实现序列化接口

                          ③添加一个<cache>在userMapper.xml

<cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>
 
 

                         ④测试+效果

package com.atguigu.mybatis.test;
 
import java.io.IOException;
import java.io.InputStream;
 
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 org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
 
import com.atguigu.mybatis.entities.User;
import com.atguigu.mybatis.mapper.UserMapper;
 
public class TestTwoLvLCache
{
 private static Logger logger = Logger.getLogger(TestTwoLvLCache.class);
 private SqlSessionFactory sqlSessionFactory = null;
  
 @Before
 public void init() throws IOException
 {
 String resource = "mybatis-config.xml";
 InputStream inputStream = Resources.getResourceAsStream(resource);
 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 }
  
 @Test
 public void test_GetUserById()
 {
//当前会话没关的时候,数据不会放在二级缓存中,会话关闭的时候会将之前所有查询过的数据放在二级缓存中,所以关闭要有顺序才能测验出缓存。会话没关,数据都是在一级缓存中放着,只有关闭了会话,才会将一级缓存中保存的数据清理到二级缓存中去
 SqlSession sqlSession1 = sqlSessionFactory.openSession();
 UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
 User user1 = userMapper1.getUserById(1);
 System.out.println(user1.toString());
 sqlSession1.close();
     
 SqlSession sqlSession2 = sqlSessionFactory.openSession();
 UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
 User user2 = userMapper2.getUserById(1);
 System.out.println(user2.toString());
 sqlSession2.close();
     
 }
 
}
 

                           2)  原理结论

             1 Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,
 不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句。
             2 Mybatis显示定义开启二级缓存,需要在setting全局参数中配置 

                     ①Cache参数解释

                         <cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>
 
 
                上面的配置意思如下:
                     创建了FIFO的策略,每个1分钟刷新一次,存512个对象而且返回的对象被认为是只读的。
 
 
                 eviction:缓存的回收策略
                flushInterval:时间间隔,单位是毫秒,
                size:引用数目,内存大就多配置点,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024
               readOnly:true,只读
 
 
            四大策略:
            1.LRU – 最近最少使用的:移除最长时间不被使用的对象,它是默认
            2.FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
           3.SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
            4.WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
                       ②禁用二级缓存

<select id="getUserById" parameterType="Integer" resultType="User" useCache="true">
  select * from tbl_user where id=#{id}
 </select>
 
设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,
默认情况是true,即该sql使用二级缓存。
                         ③刷新(清空)二级缓存

 <update id="updateUser" parameterType="User" flushCache="true">
 UPDATE tbl_user SET NAME=#{name},age=#{age} WHERE id=#{id}
 </update>
 
 insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读,所以默认为true,
默认情况下为true即刷新缓存,一般不用修改,知道即可。

                          ④ 一级缓存和二级缓存的使用顺序

           如果你的 MyBatis 使用了二级缓存,并且你的 Mapper 和 select 语句也配置使用了二级缓存,那么在执行 select 查询的时候, MyBatis 会先从二级缓存中取输入,其次才是一级缓存,即 MyBatis 查询数据的顺序是:
 
    二级缓存 ----->  一级缓存  -----> 数据库 
                           ⑤要想使某条 Select查询支持二级缓存的前提条件

1. MyBatis支持二级缓存的总开关:全局配置变量参数 cacheEnabled=true
 <settings>
   <setting name="cacheEnabled" value="true"/>
 </settings>
 
2. 该select语句所在的Mapper,配置了<cache> 节点且有效
 
3. 该select语句的参数 useCache=true 

            7.4mybatis整合Ehcache

                      1)EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,
         是Hibernate中默认的CacheProvider。

                    2)怎么用

                           ①添jar包

f78876e54159a33c92269380d2b54a7af6f.jpg

                        ②ehcache.xml配置,用自定义的

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
 
 <diskStore path="D:\44\ehcache" />
 
 <defaultCache 
   maxElementsInMemory="1000" 
   maxElementsOnDisk="10000000"
   eternal="false" 
   overflowToDisk="false" 
   timeToIdleSeconds="120"
   timeToLiveSeconds="120" 
   diskExpiryThreadIntervalSeconds="120"
   memoryStoreEvictionPolicy="LRU">
 </defaultCache>
</ehcache>
 
<!-- 
属性说明:
l diskStore:指定数据在磁盘中的存储位置。
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
 
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目 
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
 
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
 
 
 
 -->

                                ③开启ehcache缓存

修改mapper.xml文件,在cache中指定EhcacheCache
 
<!-- mybatis和ehcache结合-->
 <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

                               ④测试

import java.io.IOException;
import java.io.InputStream;
 
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 org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
 
import com.atguigu.mybatis.entities.User;
import com.atguigu.mybatis.mapper.UserMapper;
 
public class TestTwoLvLCache
{
  private static Logger logger = Logger.getLogger(TestTwoLvLCache.class);
  private SqlSessionFactory sqlSessionFactory = null;
  
  @Before
  public void init() throws IOException
  {
     String resource = "mybatis-config.xml";
     InputStream inputStream = Resources.getResourceAsStream(resource);
     sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  }
  
  @Test
  public void test_GetUserById()
  {
     SqlSession sqlSession1 = sqlSessionFactory.openSession();
     UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
     User user1 = userMapper1.getUserById(1);
     System.out.println(user1.toString());
     sqlSession1.close();
     
     SqlSession sqlSession2 = sqlSessionFactory.openSession();
     UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
     User user2 = userMapper2.getUserById(1);
     System.out.println(user2.toString());
     sqlSession2.close();
     
     SqlSession sqlSession3 = sqlSessionFactory.openSession();
     UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
     User user3 = userMapper3.getUserById(1);
     System.out.println(user3.toString());
     sqlSession3.close();
     
     SqlSession sqlSession4 = sqlSessionFactory.openSession();
     UserMapper userMapper4 = sqlSession4.getMapper(UserMapper.class);
     User user4 = userMapper4.getUserById(1);
     System.out.println(user4.toString());
     sqlSession4.close();
     
     SqlSession sqlSession5 = sqlSessionFactory.openSession();
     UserMapper userMapper5 = sqlSession5.getMapper(UserMapper.class);
     User user5 = userMapper5.getUserById(1);
     System.out.println(user5.toString());
     sqlSession5.close();     
  }
 
}

                8 Spring+Mybatis整合

                    1)spring3.0以下不支持mybatis

                    2)添jar包

                    3)db.properties

jdbc.user=root
jdbc.password=123456
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://127.0.0.1:3306/mybatis0607?useUnicode=true&amp;characterEncoding=UTF8

                     4)applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-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/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
 
 <context:component-scan base-package="com.atguigu.mybatis"></context:component-scan>
 
 <context:property-placeholder location="classpath:db.properties"/>
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
   <property name="user"     value="${jdbc.user}"/>
   <property name="password"    value="${jdbc.password}"/>
   <property name="jdbcUrl"   value="${jdbc.jdbcUrl}" />
   <property name="driverClass"  value="${jdbc.driverClass}" />
 </bean>
 
 <!-- 配置mybatis的sqlSessionFactory -->
 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <property name="dataSource" ref="dataSource"></property>
   <property name="typeAliasesPackage" value="com.atguigu.mybatis"></property>
   <property name="configLocation" value="classpath:mybatis-config.xml"/>
 </bean>
 
 <!-- 配置事务管理 -->
 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource"></property>
 </bean>
 <tx:annotation-driven transaction-manager="transactionManager"/>
 
 
</beans>
 

                     5)新建mybatis配置文件mybatis-config.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>
 
 
</configuration>

                      6)新建entity+Mapper接口/XML配置+Service

                       7) 测试调用

public interface UserMapper
{
 public void add(User user);
 
 public User getUserById(Integer id);
}
 
  
<mapper namespace="com.atguigu.mybatis.mapper.UserMapper">
 
 <insert id="add" parameterType="User">
   insert into tbl_user(name,age) values(#{name},#{age})
 </insert> 
 
 <select id="getUserById" parameterType="Integer" resultType="User">
   select * from tbl_user where id=#{id}
 </select>
 
</mapper>

              9  Mybatis逆向工程

                    9.1  是什么

                                Mybatis官方提供的逆向工程,可以将单表生生成
                      常用的Mapper、entity等配置

                    9.2 能干嘛

                              从数据库表反向生成mapper.java/mapper.xml/entity/辅助查询类

                9.3   怎么用

                             1)有数据库表提供给MyBatisGenerator反向生产素材

                            2)打开mybatis-generator-core-1.3.2-bundle\mybatis-generator-core-1.3.2\docs里面的文档照着官网配置

                            3)官方文档generatorConfig.xml

                           4)拷贝出entity/mapper/mapper.xml

                            5)测试 

@Test
 public void testselectByExample()
 {
   DogExample example = new DogExample();
   DogExample.Criteria criteria = example.createCriteria();
   criteria.andDogNameEqualTo("哈士奇");
   
   List<Dog> list = dogService.selectByExample(example);
   System.out.println(list.get(0).toString());
 }

                       9.4 注意事项

                              1 覆盖和追加问题
 1.1 工具自动化会生产3个东东,entity+Mapper接口+Mapper接口对应的xml
 1.2 假如xxxMapper.xml文件已经存在了,再次生成则mapper.xml文件内容不被覆盖而是进行内容追加,有时候会导致mybatis解析失败。所以,建议为了确保它的正确性和完整性,建议删除重建。
 
 1.3 entity和接口是因为是直接覆盖,没有上述问题。
 
 
 

转载于:https://my.oschina.net/u/3299202/blog/3071224

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值