note

  • SqlSessionFactory 它是一个线程安全的
  • SqlSession 线程非安全,不能做类的公用变量
  • 当数据库字段和实体对象名称不一至时,通过sql的字段命名别名,别名跟实体对象属性一致

jar包(mybatis3.2.2)

  • mybatis-3.2.2.jar 核心驱动
  • asm-3.3.1.jar
  • cglib-2.2.2.jar
  • commons-logging-1.1.1.jar
  • javassist-3.17.1-GA.jar
  • log4j-1.2.17.jar
  • slf4j-api-1.7.5.jar
  • slf4j-log4j12-1.7.5.jar
  • mysql-connector-java-5.1.26.jar 数据库驱动

sqlMapConfig.xml

  • 配置内容:
    1) 配置事务
    2) 配置数据源
    3) 声明mapper文件
<?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>
	<!-- 配置数据源,事务 -->
	<environments default="test">
		<environment id="test">
			<!-- 事务:JDBC/MANAGED-自己管理去 -->
			<transactionManager type="JDBC"/>
			<!-- 数据源:POOLED/UNPOOLED/JNDI -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?charsetEncoding=utf8"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
		<environment id="deploy">
			<!-- 事务:JDBC/MANAGED-自己管理去 -->
			<transactionManager type="JDBC"/>
			<!-- 数据源:POOLED/UNPOOLED/JNDI -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?charsetEncoding=utf8"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 映射文件mapper -->
</configuration>

mapper.xml

  • 命名空间,在不同的mapper,但mapper中的方法同名,靠命名空间区分
  • resultMap不是必须,可以不写,前提:表和实体的属性一样。(mybatis底层自动生成resultMap)
  • 它通过数据结果集来映射的。Hibernate它是通过数据库表字段来映射的。
  • 关联关系:
    A.对一association javaType
    B.对多 collection ofType
    jdbcType
    它是指定当NAME为null时,给jdbc驱动程序告诉它,它针对的数据库字段的类型。(Oracle)
	<update id="update" parameterType="cn.itcast.domain.Person">
		update person
		<set>
			<if test="name!=null">user_name=#{name,jdbcType=VARCHAR},</if>
			<if test="age!=null">age=#{age},</if>
			<if test="remark!=null">remark=#{remark},</if>
		</set>
		where id = #{id}
	</update>
  • Mapper中写SQL获取参数
	+ #{} 它会自动根据参数类型做封装。例如对字符串类型,两边加单撇;对整数类型直接使用;好处防止SQL注入(推荐)

	+ ${} 将用户填入的参数直接拼接到SQL。(字符串直接拼接)坏处:SQL注入;例如:拼接order by条件(特殊的地方)
  • SQL中含有特殊字符
<if test="ageEnd!=null"><![CDATA[and age <=]]> #{ageEnd}</if>
  • 动态SQL
	+ <if>	判断条件
	+ <where> 自动删除最前面的and 或者or,实际开发中常用where 1=1
	+ <set> 它自动删除最后一个set值的逗号,修改时,修改参数不同
	+ <foreach> 主要应用在in子查询,批量删除时候,array,list,map
  • 常规的标签
	+ <select>	查询,参数map;返回值List(selectList),可以是一个值(selectOne)
	+ <insert> 新增,参数po;新增时习惯写所有的字段
	+ <update> 修改,参数po;修改时习惯使用动态sql语句
	+ <delete> 删除,参数 int,string,array,list,map
  • 参数:

    • parameterMap 废除,早期使用
    • parameterType 简单变量int/string/double/po/map
  • 返回值:

    • resultType 简单变量int/string/double/po/map
    • resultMap 它是mybatis核心;实际resultType使用时,底层自动转为resultMap;在数据库结果集和实体对象加了一层。例如:数据库字段USER_NAME,实体中name;通过它mybatis可以找到映射的规则;它可以配置关联关系;mybatis自动将大大的结果集分拆到各个对象中。(难点)
    • SQL返回的结果集中字段不能有重名的,如果有,必须设置别名,通过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">

<!-- 配置命名空间,区别名称 -->
<mapper namespace="com.fuyi.mapper.PersonMapper">

	<!-- 查询,注意Mybatis中如果有填写集合类型,只填写集合中元素的类型  -->
	<sql id="cols">id,name,age</sql>

	<resultMap type="com.fuyi.domain.User" id="userRM">
		<id property="id" column="ID" />
		<result property="name" column="NAME" />
		<result property="age" column="AGE" />
	</resultMap>

	<resultMap type="com.fuyi.domain.User" id="userAndInfoRM"
		extends="userRM">
		<association property="userInfo" javaType="com.fuyi.domain.UserInfo">
			<id property="id" column="info_id" />
			<result property="jobDate" column="job_date" />
			<result property="position" column="POSITION" />
		</association>
	</resultMap>

	<resultMap type="com.fuyi.domain.User" id="userAndBookRM"
		extends="userAndInfoRM">
		<collection property="book" ofType="com.fuyi.domain.Book">
			<id property="id" column="book_id" />
			<result property="title" column="title" />
			<result property="publish" column="publish" />
		</collection>
	</resultMap>

	<select id="findUserAndBook" resultMap="userAndBookRM">
		select p.id, p.name,
		p.age, p.info_id, p.job_date,p.position, b.id as book_id, b.title,
		b.publish
		from (select u.id,u.name,u.age,i.id as info_id,i.job_date,i.position
		from
		user u left outer join user_info i on u.id = i.user_id ) p LEFT JOIN
		book b on p.id = b.user_id;
	</select>




	<select id="findUserAndInfo1" resultMap="userAndInfoRM">
		select
		u.id,u.name,u.age,i.id as info_id,i.job_date,i.position
		from user u
		left outer join user_info i on u.id = i.user_id
	</select>



	<!-- 查询,注意Mybatis中如果有填写集合类型,只填写集合中元素的类型 -->
	<select id="find" parameterType="map" resultType="User">
		select
		<include refid="cols" />
		from user
		<where>
			<if test="name!=null">and name like #{name}</if>
			<if test="age!=null">and age = #{age}</if>
		</where>

	</select>

	<select id="get" parameterType="int" resultType="com.fuyi.domain.User">
		select * from
		user
	</select>

	<insert id="insert" parameterType="com.fuyi.domain.User">
		insert into user(name, age)
		values(#{name}, #{age})
	</insert>

	<update id="update" parameterType="com.fuyi.domain.User">
		update user
		<!-- name = #{name}, age = #{age} -->
		<set>
			<if test="name!=null"> name = #{name},</if>
			<if test="age!=null"> age = #{age},</if>
		</set>
		where id = #{id}
	</update>

	<delete id="deleteById" parameterType="int">
		delete from user
		where id in
		<foreach collection="array" item="ind" open="(" close=")"
			separator=",">
			#{ind}
		</foreach>
	</delete>

	<delete id="deleteList" parameterType="int">
		delete from user
		where id in
		<foreach collection="list" item="ind" open="(" close=")"
			separator=",">
			#{ind}
		</foreach>
	</delete>

	<delete id="deleteMap" parameterType="map">
		delete from user
		where id in
		<foreach collection="ids" item="id" open="(" close=")"
			separator=",">
			#{id}
		</foreach>
	</delete>

	<select id="count" resultType="int">
		select count(*) from user
	</select>
</mapper>

测试

public class TestMybatis {
	@Test
	public void init() throws IOException{
		InputStream is = Resources.getResourceAsStream(classpath:mybatis/sqlMapConfig.xml);
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
		SqlSession session = factory.openSession();

		//如何访问mapper中的方法呢?  规则:命名空间+.+id
		List<Person> personList = session.selectList("com.fuyi.mapper.PersonMapper.find");
		System.out.println(personList.size());
		for(Person p : personList){
			System.out.println(p);
		}

	}
}

mapper接口开发

  • 规则
    • 接口名称必须和mapper文件名称相同
    • 接口包路径和命名空间一致(接口文件和mapper文件在一起)
    • 接口中要声明的方法名和某个sql配置的id属性一致
    • 参数和返回值,要按java规则写
  • Test
public class TestMapperInterface {
	private SqlSessionFactory factory;
	
	@Before	//最先执行,初始化SessionFactory
	public void init() throws IOException{
		String resource = "sqlMapConfig.xml";
		InputStream is = Resources.getResourceAsStream(resource);
		factory = new SqlSessionFactoryBuilder().build(is);
	}
	
	@Test
	public void testFind(){
		SqlSession session = factory.openSession();
		//获取接口对象
		PersonMapper mapper = session.getMapper(PersonMapper.class);
		Map map = new HashMap();
		map.put("name", "'t%'");
		
		List<Person> pList = mapper.find(map);
		System.out.println(pList.size());
	}
}

缓存

- 一级缓存Hibernate,session级别
- 二级缓存Hibernate,sessionFactory级别
- Mybatis它和hibernate一样

- 一级缓存默认开启

	public void cacheLevel1(){
		SqlSession session = factory.openSession();
		PersonMapper mapper = session.getMapper(PersonMapper.class);
		Map map = new HashMap();
		map.put("name", "'tony'");
		
		List<Person> personList = mapper.find(map);
		System.out.println(personList.size());
		
		PersonMapper mapper2 = session.getMapper(PersonMapper.class);
		
		List<Person> personList2 = mapper2.find(map);
		System.out.println(personList2.size());
		//结果只发出一条sql查询
	}
  • 二级缓存默认不开启
开启方式:在mapper.xml中添加  
<cache
	eviction="FIFO"
	flushInterval="60000"
	size="512" 
    readOnly="true"/>
在实际开发中,经过在线上压力测试,找到一个符合当前客户运行环境下最恰当的值。

	public void cacheLevel2(){
		SqlSession session = factory.openSession();
		PersonMapper mapper = session.getMapper(PersonMapper.class);
		Map map = new HashMap();
		map.put("name", "'tony'");
		
		List<Person> personList = mapper.find(map);
		System.out.println(personList.size());
		
		session.close();	//关闭session
		session = factory.openSession();
		
		PersonMapper mapper2 = session.getMapper(PersonMapper.class);
		
		List<Person> personList2 = mapper2.find(map);
		System.out.println(personList2.size());
		// 关闭session后,结果只发出一条sql查询
	}

默认mybatis二级缓存一般使用第三方Ehcache

- Ehcache 单机
- Momeycache 集群

- 导入Ehcache的jar包
- ehcache-core-2.6.5.jar
- mybatis-ehcache-1.0.2.jar

<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

- copy ehcache-failsafe.xml rename ehcache.xml

拦截器interceptor

- Mybatis 分页

1)	自定义一个拦截器类,实现interceptor接口
2)	插件,在sqlMapConfig.xml中配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
	PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 分页拦截器,拦截用户提交的查询,只查询当前页数据;提高响应速度。 -->
	<plugins>  
		<plugin interceptor="cn.itcast.jk.pagination.PageInterceptor">  
			<property name="databaseType" value="oracle"/>  
		</plugin>  
	</plugins>  
</configuration>	
  • Springmvc 拦截器
性能监控
开发步骤:
1、编写拦截器,记录Controller方法执行时间

public class TimerInterceptor implements HandlerInterceptor {
	private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("WatchExecuteTime");
	
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
		long beginTime = System.currentTimeMillis();			//开始时间
		startTimeThreadLocal.set(beginTime);
		return true;
	}
	
	public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object arg2, Exception arg3) throws Exception {
		long endTime = System.currentTimeMillis();
		long executeTime = endTime - startTimeThreadLocal.get();
		System.out.println(String.format("%s execute %d ms." , req.getRequestURI() , executeTime));
	}
	
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
			Object arg2, ModelAndView arg3) throws Exception {
	}
}

2、在springmvc-servlet.xml文件中配置拦截器


对多个controller进行指定方法的拦截
	<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/cargo/export/list.action"/>
			<bean class="cn.itcast.jk.interceptor.TimerInterceptor"/>
		</mvc:interceptor>
		<mvc:interceptor>
			<mvc:mapping path="/cargo/packinglist/list.action"/>
			<bean class="cn.itcast.jk.interceptor.TimerInterceptor"/>
		</mvc:interceptor>
	</mvc:interceptors>


对多个controller的所有方法拦截
	<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/cargo/export/*"/>
			<bean class="cn.itcast.jk.interceptor.TimerInterceptor"/>
		</mvc:interceptor>
		<mvc:interceptor>
			<mvc:mapping path="/cargo/packinglist/*"/>
			<bean class="cn.itcast.jk.interceptor.TimerInterceptor"/>
		</mvc:interceptor>
	</mvc:interceptors>


对某目录下的controller进行拦截

	<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/cargo/**"/>
			<bean class="cn.itcast.jk.interceptor.TimerInterceptor"/>
		</mvc:interceptor>
	</mvc:interceptors>

 拦截整个项目的所有controller

	<mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/**"/>
			<bean class="cn.itcast.jk.interceptor.TimerInterceptor"/>
		</mvc:interceptor>
	</mvc:interceptors>

spring + mybatis

  • sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

</configuration>

  • beans.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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
				         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
				         http://www.springframework.org/schema/context
				         http://www.springframework.org/schema/context/spring-context-3.0.xsd
				         http://www.springframework.org/schema/tx
				         http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
				         http://www.springframework.org/schema/aop 
				         http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<!-- 1.包扫描,service dao -->
	<context:component-scan base-package="cn.itcast.ssm.dao,cn.itcast.ssm.service"/>
	
	<!-- 2.jdbc.properties属性配置文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	<!-- 3.数据源 DataSource -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverclass}"/>
		<property name="url" value="${jdbc.url}"/>
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
	
	<!-- 4.SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<!-- spring和mybatis整合 -->
		<!-- 1)引入mybatis核心配置文件 -->
		<property name="configLocation" value="classpath:sqlMapCongfig.xml"/>
		
		<!-- 2)扫描指定目录下的所有Mapper.xml -->
		<property name="mapperLocations" value="classpath:cn/itcast/ssm/mapper/*.xml"/>
	</bean>
	
	<!-- 5.事务 -->
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="insert*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			
			<tx:method name="find*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="view*" read-only="true"/>
			
			<tx:method name="*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	<aop:config>
		<aop:pointcut expression="execution(* cn.itcast.ssm.service.*.*(..))" id="pointCut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointCut"/>
	</aop:config>
			         
</beans>

  • springmvc-servlet.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:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/mvc 
	http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd 
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context-3.0.xsd 
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">
	
	<!-- 1.包扫描controller -->
	<context:component-scan base-package="cn.itcast.ssm.web.controller"/>
	
	<!-- 2.内部资源视图解析器  -->
	<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/pages"/>
		<property name="suffix" value=""/>
	</bean>
	
</beans>
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="taotao" version="2.5">
	<display-name>yycg</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
	<!-- 加载spring容器  -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring/applicationContext*.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>


	<!-- 解决post乱码 -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>utf-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param> 
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>


	<!-- springmvc的前端控制器  -->
	<servlet>
		<servlet-name>yycg</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		
		<!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml"  -->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring/springmvc.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>yycg</servlet-name>
		<!-- 做伪静态,做搜索引擎优化(SEO)
		<url-pattern>*.html</url-pattern>
		-->
		<url-pattern>*.action</url-pattern>
	</servlet-mapping>
	 
</web-app>