spring的声明式事务管理,可以回滚service的操作(当遇到异常情况时)
Class Log{
Propagation.REQUIRED
insertLog();
}
Propagation.REQUIRED
void saveDept(){
insertLog();//加入当前事务
..异常部分
saveDept();
}
Class LOg{
Propagation.REQUIRED_NEW
insertLog();
}
Propagation.REQUIRED
void saveDept(){
insertLog();//始终开启事务
..异常部分,日志不会回滚
saveDept();
}
Aop(aspect object programming): 面向切面编程
- 功能:让关注点代码与业务代码分离!
- 关注点: 重复代码就叫做关注点;
- 切面: 关注点形成的类,就叫切面(类).
- 面向切面编程,就是指对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入“切面类代码”。
- 切入点: 执行目标对象方法,动态植入切面代码。可以通过切入点表达式,指定拦截哪些类的哪些方法;给指定的类在运行的时候植入切面类代码。(根据需要进行拦截,是否需要拦截)切入点表达式,可以对指定的“方法”进行拦截; 从而给指定的方法所在的类生层代理对象。
spring 代理理解
动态代理,用工厂类实现--需要实现接口
1.测试类
ApplicationContext ac = new ClassPathXmlApplicationContext("com/coffee/aop/bean.xml");
/**
* 动态代理(jdk代理)
*
* @throws Exception
*/
@Test
public void testAop() throws Exception {
// 调用工厂类,获取注解
IUserDao userDao = (IUserDao) ac.getBean("userDaoProxy");
// 代理对象:class com.sun.proxy.$Proxy5
System.out.println(userDao.getClass());
userDao.save();
}
2.代理工厂类--用反射实现
@Component("userDaoProxy")
public class ProxyFactory {
private static Object target;
private static Aop aop;
public static Object getProxyInstance(Object target_, Aop aop_) {
target = target_;
aop = aop_;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] objects) throws Throwable {
// 在核心业务代码执行前,,引入重复执行的代码
aop.begin();
Object returnValue = method.invoke(target, objects);
// 核心代码结束后执行收尾工作
aop.commit();
return returnValue;
}
});
}
动态代理--不需要使用工厂类,用注解
1.dao层,加注解@Repository("userDao")
@Repository("userDao")
public class UserDao implements IUserDao {
public void save() {
// 获取session/处理异常--每次都要重复执行此类代码:被称为【关注点代码:就是重复执行的代码】
System.out.println("UserDao 核心业务代码:保存数据。。");// 这才是真正的核心业务代码:【关键点代码】
// 关闭session/处理异常--每次都要重复执行此类代码:【关注点代码:就是重复执行的代码】
}
2.bean.xml
首先开启注解扫描
<context:component-scan base-package="com.coffee.aop"></context:component-scan>
然后开启aop自动实现代理
<!-- 注解实现 aop编程 -->
<!-- 1.在命名空间引入 aop相关头 -->
<!-- 2.开启 aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
cglib代理:实现的cglib代理的类不能是final
cglib代理:需要引入spring-core.jar文件
1.测试类
/**
* 注解代理:目标类没有实现接口,aop自动执行 cglib代理
*
* @throws Exception
*/
@Test
public void testCglibAop() throws Exception {
// 调用工厂类
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
// 代理对象:class com.sun.proxy.$Proxy5
System.out.println(orderDao.getClass());
orderDao.save();
}
2.dao层加注解
// 将目标对象加入ioc
@Repository("orderDao")
public class OrderDao {// 没有实现接口,使用cglib代理
public void save() {
System.out.println("OrderDao 核心业务代码:保存数据。。");// 这才是真正的核心业务代码:关键点代码
}
3.bean.xml配置注解,开启aop
首先开启注解扫描
<context:component-scan base-package="com.coffee.aop"></context:component-scan>
然后开启aop自动实现代理
<!-- 注解实现 aop编程 -->
<!-- 1.在命名空间引入 aop相关头 -->
<!-- 2.开启 aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
cglib底层实现
public class ProxyFactory_cglib implements MethodInterceptor {
private Object target;// 维护代理对象
public ProxyFactory_cglib(Object target) {
this.target = target;
}
// 给目标对象创建代理对象
public Object getProxyInstance() {
// 1. 工具类,在引入的jar文件中spring-core.jar
Enhancer enhancer = new Enhancer();
// 2. 设置父类
enhancer.setSuperclass(target.getClass());
// 3. 设置回掉函数
enhancer.setCallback(this);
// 4. 创建子类代理对象,,,,所以使用cglib代理的dao不能是final的
return enhancer.create();
}
关注点代码&&关键点代码
dao层
public void save() {
// 获取session/处理异常--每次都要重复执行此类代码:被称为【关注点代码:就是重复执行的代码】
System.out.println("UserDao 核心业务代码:保存数据。。");// 这才是真正的核心业务代码:【关键点代码】
// 关闭session/处理异常--每次都要重复执行此类代码:【关注点代码:就是重复执行的代码】
}
切入点表达式
bean.xml:
<!-- 配置aop -->
<aop:config>
<!-- 定义一个切入点表达式 ,指定拦截哪些类的哪些方法-->
<aop:pointcut expression="execution(* com.coffee.pointcut.*.*(..))" id="pt"/>
<!-- 从这开始#######【下面是测试别的方法】######### -->
<!-- 拦截所有的public方法-->
<aop:pointcut expression="execution(public * *(..))" id="pt"/>
<!-- 拦截所有的save方法-->
<aop:pointcut expression="execution(* save*(..))" id="pt"/>
<!-- 拦截指定类的指定的指定方法,具体到方法-->
<aop:pointcut expression="execution(* com.coffee.pointcut.OrderDao.save(..))" id="pt"/>
<!-- 拦截指定类的指定的所有方法,具体到类-->
<aop:pointcut expression="execution(* com.coffee.pointcut.OrderDao.*(..))" id="pt"/>
<!-- 拦截指定包,及其子包下的的所有类的所有方法 -->
<aop:pointcut expression="execution(* com..*.*(..))" id="pt"/>
<!-- 【多个表达式】 -->
<!-- 拦截或的关系的方法,不能用and 或者&& -->
<aop:pointcut expression="execution(* com.coffee.pointcut.OrderDao.save()) || execution (* com.coffee.pointcut.UserDao.save())" id="pt"/>
<aop:pointcut expression="execution(* com.coffee.pointcut.OrderDao.save(..))) || execution(* com.coffee.pointcut.UserDao.save())" id="pt"/>
<!-- 取非值 -->
<aop:pointcut expression="!execution(* com.coffee.pointcut.OrderDao.save())" id="pt"/>
<!-- not 前要加空格,和上面等价 -->
<aop:pointcut expression=" not execution(* com.coffee.pointcut.OrderDao.save())" id="pt"/>
<!-- 从这结束################ -->
<!-- 切面配置 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"/>
<!-- 前置通知 -->
<aop:before method="begin" pointcut-ref="pt"/>
<!-- 后置通知(最终通知) -->
<aop:after method="after" pointcut-ref="pt"/>
<!-- 返回后通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
<!-- 异常的通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
aop切面类:
public class Aop {
public void begin() {
System.out.println("开始事务/异常");
}
public void after() {
System.out.println("提交事务/关闭");
}
public void afterReturning() {
System.out.println("afterReturning()");
}
// 目标方法异常处理
public void afterThrowing() {
System.out.println("afterThrowing()");
}
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前执行。。相当于@Before()");
pjp.proceed();
System.out.println("还绕后执行。。相当于@After()");
}
}
// 事务传播的属性
@Service
public class T_DeptService {
@Resource
// 加入容器
T_DeptDao t_DeptDao = new T_DeptDao();
@Resource
LogsDao logsDao = new LogsDao();
// 事务传播的属性
@Transactional(
// readOnly = false,
// timeout = -1,
// noRollbackFor = ArithmeticException.class 遇到异常不回滚
// propagation=Propagation.REQUIRED Propagation.REQUIRED
// 指定当前的方法必须在事务的环境下执行;
// 如果当前运行的方法,已经存在事务, 就会加入当前的事务,受当前事务约束;
// Propagation.REQUIRED_NEW
// 指定当前的方法必须在事务的环境下执行;
// 如果当前运行的方法,已经存在事务: 事务会挂起(就像遇到异常不回滚此方法); 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行。
)
// 必须加上这个注解才能实现注解的spring事务控制,这个注解可以加载类上,父类上,范围范围根据加在什么上面而不同
public void save(T_Dept t_Dept) {
logsDao.insertlog();
int i = 1 / 0;// 模拟中间的异常,配置spring事务控制后遇到异常就会回滚,即上面的数据库操作无效
t_DeptDao.save(t_Dept);
}
<!-- 1.【c3p0连接池 数据源配置(oracle)】 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:orcl"></property>
<property name="user" value="wangan"></property>
<property name="password" value="666"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!-- 2.【数据库模板】 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
pring声明式事务管理
<!-- ####5. spring声明式事务管理器### -->
<!-- 1. 配置事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2. 配置事务增强 ,如何管理事务-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 以get开头/find开头的所有方法只读,剩下的读写 -->
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 3. 配置aop,拦截哪些方法(切入点表达式)+应用上面的事务增强 -->
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.coffee.spring_transaction.T_DeptService.*(..))" /><!-- (..)带上两个点;第一个*式返回值类型 -->
<!-- 引入切入点表达式 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
</aop:config>
mybatis中的sqlsession工具类
package utils;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* 加载mybatis配置文件
* @author wangan
*
*/
public class MybatisUtil {
// 本地线程,用于绑定session,,SqlSession是mybatis里面的创建session的类,hibernate是session
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
private static SqlSessionFactory sqlSessionFactory;
// 私有化无参构造,防止人为不断new他
private MybatisUtil() {
}
// 使用static静态块的好处就是加载快,只能加载一次
static {
try {
// 加载src/mybatis.xml
Reader reader = Resources.getResourceAsReader("mybatis.xml");
// 加载reader,创建sqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 返回session
*/
public static SqlSession getSqlSession() {
SqlSession sqlSession = threadLocal.get();
if (sqlSession == null) {
sqlSession = sqlSessionFactory.openSession();
// 本地线程绑定sqlsession
threadLocal.set(sqlSession);
}
return sqlSession;
}
/**
* 关闭session
*/
public static void closeSqlSession() {
SqlSession sqlSession = threadLocal.get();
if (sqlSession != null) {
// 关闭session
sqlSession.close();
// 移除session,供GC回收,不然多次访问数据库后会变慢
threadLocal.remove();
}
}
}
映射配置
<?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="studentNamespace">
<!-- type="com.coffee.mybatis01.entity.Student"名字太长,用别名在mybatis配置文件配置
id属性用来标记resultmap,如果实体类中的属性和表的字段不一致,用id
如果都一致,可选
-->
<resultMap type="student" id="student">
<!-- 主键映射 -->
<id property="id" column="id" />
<!-- 其他属性映射 -->
<result property="name" column="name"/>
<result property="sal" column="sal"/>
</resultMap>
<!--
StudentDao里面的add方法,就是插入方法名称(理论上随便起,方便理解就用一样的名字)
parameterType方法的参数的类型,写全路径
-->
<!-- 无参插入( parameterType="com.coffee.mybatis01.entity.Student"省略) -->
<insert id="add1" >
<!-- 插入的sql语句 -->
insert into students values(1,'张三',7000)
</insert>
<!-- 带参插入 -->
<insert id="add2" parameterType="student">
<!-- 插入的sql语句 #{id}==student.getId(),是占位符的意思,hibernate是:= -->
insert into students values(#{id},#{name},#{sal})
</insert>
<!-- 修改值 -->
<update id="update" parameterType="student">
update students set name=#{name},sal=#{sal} where id=#{id}
</update>
<!-- 查询所有
resultType返回的是集合,但是我就是要遍历出来student,所以只写student类型
-->
<select id="findAll" resultType="student">
select * from students
</select>
<!-- 根据id查询
resultMap映射resultMap里的id,代表返回值类型,根据id的类型进行返回值判断,resultType比较精确,resultMap是通用的
-->
<select id="findById" parameterType="int" resultMap="student">
select * from students where id=#{id}
</select>
<!-- 删除数据 -->
<delete id="delete" parameterType="int">
delete from students where id=#{id}
</delete>
<!-- ###分页操作 -->
<!-- 无条件分页 -->
<select id="findPage" resultMap="student">
select * from students where id between #{start} and #{end}
</select>
<!-- 动态sql查询 -->
<select id="dynaSelect" parameterType="map" resultMap="student">
select * from students
<where>
<!-- kid是map的Key键,test自动判断key值 -->
<if test="pid!=null">
and id=#{pid}
</if>
<if test="pname!=null">
and name=#{pname}
</if>
<if test="psal!=null">
and sal=#{psal}
</if>
</where>
</select>
<!-- 动态sql update更新
name=#{name},不要忘了逗号','
-->
<update id="dynaUpdate" parameterType="student">
update students
<set>
<if test="name!=null">
name=#{name},
</if>
<if test="sal!=null">
sal=#{sal}
</if>
</set>
where id=#{id}
</update>
<!-- 动态sql删除多个#迭代数组 -->
<delete id="dynaDelete">
<!-- 删除多个需要遍历数组(这里是数组的遍历array)
完整的sql是: delete from students where id in (1,3,5)
collection遍历的类型:array/list
open开头 close结尾
item遍历的名称,可以任意写,不需要和dao的一致
separator分隔符,分割数组的内容
#{ids}和item的一致
-->
delete from students where id in
<foreach collection="array" open="(" close=")" separator="," item="ids">
#{ids}
</foreach>
</delete>
<!-- 动态sql删除多个,迭代list -->
<delete id="dynaDeleteList">
delete from students where id in
<foreach collection="list" open="(" close=")" item="list" separator=",">
#{list}
</foreach>
</delete>
<!-- #### 动态插入对象 #######-->
<!--
insert into students (id,name,value) values (1,"张三",5000.0)
-->
<!-- <sql id="key">
<if test="id!=null">id,</if>
<if test="name!=null">name,</if>
<if test="sal!=null">sal</if>
</sql>
<sql id="value">
trim标签可以去掉,同时把#{sal},的逗号去掉
<trim suffixOverrides=",">
<if test="id!=null">#{id},</if>
<if test="name!=null">#{name},</if>
<if test="sal!=null">#{sal},</if>
</trim>
</sql>
<insert id="dynaInsert">
insert into students (<include refid="key"></include>)
values(<include refid="value"></include>)
</insert> -->
<!-- 效果同上,都可以插入 -->
<insert id="dynaInsert">
insert into students (id,name,sal)
values(#{id},#{name},#{sal})
</insert>
</mapper>
dao调用
package com.coffee.mybatis01.dao;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import utils.MybatisUtil;
import com.coffee.mybatis01.entity.Student;
/**
* 数据访问层
* @author wangan
*
*/
public class StudentDao {
/**
* 添加学生--无参
* @param student
*/
public void add1() throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
sqlSession.insert("studentNamespace.add1");
try {
sqlSession.commit();
} catch (Exception e) {
// 回滚操作
sqlSession.rollback();
throw new RuntimeException(e);
} finally {
MybatisUtil.closeSqlSession();
}
}
/**
* 添加学生--有参
* @param student
*/
public void add2(Student student) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 插入对象,指定映射空间名称,和<insert>标签的id
try {
sqlSession.insert("studentNamespace.add2", student);
} catch (Exception e) {
sqlSession.rollback();
throw new RuntimeException(e);
} finally {
sqlSession.commit();
MybatisUtil.closeSqlSession();
}
}
/**
* 修改学生
* @param student
*/
public void update(Student student) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
sqlSession.update("studentNamespace.update", student);
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.commit();
MybatisUtil.closeSqlSession();
}
}
/**
* 查询所有学生
* @param student
*/
public List<Student> findAll() throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<Student> studentList = new ArrayList<Student>();
try {
studentList = sqlSession.selectList("studentNamespace.findAll");
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.commit();
MybatisUtil.closeSqlSession();
}
return studentList;
}
/**
* 根据id查询学生
* @param student
*/
public Student findById(int id) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
Student student = new Student();
try {
student = sqlSession.selectOne("studentNamespace.findById", id);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
MybatisUtil.closeSqlSession();
}
return student;
}
/**
* 删除学生
* @param student
*/
public void delete(int id) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
sqlSession.delete("studentNamespace.delete", id);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
MybatisUtil.closeSqlSession();
}
}
/**
* 分页查询--无条件
*/
public List<Student> findPage(int start, int end) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<Student> students = new ArrayList<Student>();
try {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("start", start);
map.put("end", end);
students = sqlSession.selectList("studentNamespace.findPage", map);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
MybatisUtil.closeSqlSession();
}
return students;
}
/**
* 动态sql查询--参数使用包装类型进行条件判断时如果是null代表不限,不确定,任意
*/
public List<Student> dynaSelect(Integer id, String name, Double sal)
throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<Student> list = new ArrayList<Student>();
try {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("pid", id);
map.put("pname", name);
map.put("psal", sal);
list = sqlSession.selectList("studentNamespace.dynaSelect", map);
} catch (Exception e) {
sqlSession.rollback();
throw new RuntimeException(e);
} finally {
MybatisUtil.closeSqlSession();
}
return list;
}
/**
* 动态sql更新
*/
public void dynaUpdate(Student student) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
sqlSession.update("studentNamespace.dynaUpdate", student);
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
sqlSession.commit();
MybatisUtil.closeSqlSession();
}
}
/**
* 动态sql迭代数组--根据id删除多个
*/
public void dynaDelete(int... ids) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
sqlSession.delete("studentNamespace.dynaDelete", ids);
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
sqlSession.commit();
MybatisUtil.closeSqlSession();
}
}
/**
* 动态sql迭代list集合--根据id删除多个
*/
public void dynaDeleteList(List<Integer> list) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
sqlSession.delete("studentNamespace.dynaDeleteList", list);
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
sqlSession.commit();
MybatisUtil.closeSqlSession();
}
}
/**
* 动态sql插入对象
*/
public void dynaInsert(Student student) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
sqlSession.insert("studentNamespace.dynaInsert", student);
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
sqlSession.commit();
MybatisUtil.closeSqlSession();
}
}
}
整合之注册功能
spring.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- 1. 配置c3p0连接池 -->
<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver"/>
<property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
<property name="user" value="wangan"/>
<property name="password" value="666"/>
</bean>
<!-- 2. 配置sqlsession代替原生mybatisUtil工具类 -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 加载配置文件mybatis.xml -->
<property name="configLocation" value="classpath:mybatis.xml"/>
<!-- 引入数据资源 -->
<property name="dataSource" ref="comboPooledDataSource"/>
</bean>
<!-- 3. mybatis事务管理器,底层用的是jdbc -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 引入数据源 -->
<property name="dataSource" ref="comboPooledDataSource"/>
</bean>
<!-- 4. 配置事物通知,如何管理事物 -->
<tx:advice id="tx" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<!-- REQUIRED默认,在有事物情况下执行,没有事物就创建新的事物
propagation="REQUIRED" 默认
read-only="false" 默认
-->
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 5.配置事物切面aop,拦截哪些方法 -->
<aop:config>
<aop:pointcut expression="execution(* com.coffee.dao.*.*(..))" id="pointcut"/>
<aop:advisor advice-ref="tx" pointcut-ref="pointcut"/>
</aop:config>
<!-- 注册dao -->
<bean id="studentDao" class="com.coffee.dao.StudentDao">
<property name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
</bean>
<!-- 注册service -->
<bean name="studentService" class="com.coffee.service.StudentService">
<property name="studentDao" ref="studentDao"></property>
</bean>
<!-- 注册action(注解),里面有service,service加了注解,扫描 -->
<context:component-scan base-package="com.coffee"/>
<!-- 通知springioc注解作用 -->
<context:annotation-config />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
dao
package com.coffee.dao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.coffee.entity.Student;
/**
* 数据访问--StudentDao
* @author wangan
*
*/
public class StudentDao {
// 注入sqlsession工厂
private SqlSessionFactory sqlSessionFactory;
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
/**
* 添加学生
* @param student
* @throws Exception
*/
public void add(Student student) throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("studentNamespace.add", student);
sqlSession.close();
}
}
service
package com.coffee.service;
import com.coffee.dao.StudentDao;
import com.coffee.entity.Student;
/**
* 数据访问
* @author wangan
*
*/
public class StudentService {
private StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
public void register(Student student) throws Exception {
try {
studentDao.add(student);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Action
package com.coffee.action;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.coffee.entity.Student;
import com.coffee.service.StudentService;
/**
* action
* @author wangan
*
*/
@Controller
@RequestMapping(value = "/student")
public class StudentAction {
private StudentService studentService;
@Resource(name = "studentService")
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
/**
* 注册学生
* @param student
* @return
*/
@RequestMapping(value = "/register")
public String registerStudent(Student student) {
try {
studentService.register(student);
} catch (Exception e) {
e.printStackTrace();
}
return "success";
}
}
test
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.coffee.dao.StudentDao;
import com.coffee.entity.Student;
/**
* 测试整合
* @author wangan
*
*/
public class TestSpring_mybatis {
public static void main(String[] args) {
Student student = new Student(20, "王林", 8000d);
ApplicationContext ac = new ClassPathXmlApplicationContext(
new String[] { "spring.xml" });
StudentDao studentDao = (StudentDao) ac.getBean("studentDao");
try {
studentDao.add(student);
} catch (Exception e) {
e.printStackTrace();
}
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>sshProject2</display-name>
<!-- struts2 配置 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- spring配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:bean.xml</param-value>
</context-param>
<listener>
<!-- 服务器启动时自动装配spring的配置bean.xml -->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
struts.xml
<!-- 拦截器 -->
<interceptors>
<interceptor name="userInterceptor" class="com.coffee.action.UserInterceptor"></interceptor>
<interceptor-stack name="myStack">
<!-- 配置struts2框架运行时,默认执行自定义拦截器栈 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 应用自定义拦截器 -->
<interceptor-ref name="userInterceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 执行指定的拦截器 -->
<default-interceptor-ref name="myStack"></default-interceptor-ref>
UserInterceptor.java
package com.coffee.action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
/**
* 管理员拦截器
* 较验用户是否登陆,只有登陆后才可以进行操作。
* 没有登陆,只能查看列表,不能操作!
* @author wangan
*
*/
@SuppressWarnings("all")
public class UserInterceptor extends AbstractInterceptor {
@Override
// 所有的拦截器都会调用此方法
public String intercept(ActionInvocation invocation) throws Exception {
// 1.得到当前执行的方法--代理获取方法
String methodName = invocation.getProxy().getMethod();
// 2.得到actionContext对象
ActionContext context = invocation.getInvocationContext();
// 3.获取session,从session获取登录用户对象
Object object = context.getSession().get("adminInfo");
// 4.判断方法是否放行,登录方法放行
if (!"login".equals(methodName) && !"list".equals(methodName)) {
if (object == null) {
// 没有登录
return "login";
} else {
// 执行action ,放行
return invocation.invoke();
}
} else {
// 允许访问登录/列表展示
return invocation.invoke();
}
}
}
数据回显
/**
* 3. 修改员工-进入修改页面(list.jsp里面的修改 链接跳转到这里)
* struts2的保存修改数据的方式就是模型驱动,先把旧的数据移除,新的压进栈
*/
public String viewUpdate() {// 更新一条记录的关键步骤
// 1.获取主键
int id = employee.getEmployeeId();
// 2.根据员工的主键查询,此时已经有部门信息(lazy=false)
Employee employee = employeeService.findById(id);
// 3. 查询所有的部门信息
List<Dept> listDept = deptService.getAll();
// 4.数据回显
// 获取valueStack对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
// 移除栈顶元素(旧的)
valueStack.pop();
// 入栈,即将更新的值
valueStack.push(employee);
request.put("listDept", listDept);
return "edit";
}
下拉列表
<td>
<!-- name="deptId"随便取,代表下拉列表名字 -->
<s:select list="#request.listDept" listKey="deptId" listValue="deptName" name="deptid" headerKey="-1" headerValue="请选择" value="-1"></s:select>
</td>
做注册的时候员工的信息联系到另外一张表部门表,下拉菜单选择部门的时候这个deptid顺带传过去提交到注册action
// 封装部门id,下拉列表里面的name=“deptid”的值
private int deptid;
public void setDeptid(int deptid) {
this.deptid = deptid;
}
public int getDeptid() {
return deptid;
}
//注册
public String save() {
// 先根据部门主键查询
Dept dept = deptService.findById(deptid);
// 部门设置到员工对象中
employee.setDept(dept);
// 保存员工
employeeService.save(employee);
return "listAction";// 重定向到Action
}
mybatis工作流程:
1️⃣ 通过Reader对象读取src目录下面的mybatis.xml配置文件(可自定义路径)
2️⃣ 通过SqlSessionBuilder对象创建SqlSessionFactory对象
3️⃣ 从当前线程中获取SqlSession对象
4️⃣ 事物开始,在mybatis中默认
5️⃣ 通过SqlSession对象读取StudentMapper.xml映射文件中的操作编号,从而读取sql语句
6️⃣ 事物必须提交
7️⃣ 关闭SqlSession对象.并且分开当前线程与SqlSession对象,让GC尽早回收
批量插入数据--list集合
<insert id="insertList">
INSERT INTO BUY_ORDER_DETAIL (BOD_ID, GOODS_ID, GOODS_NAME,
GOODS_UNIT, GOODS_TYPE, GOODS_COLOR,
BOD_AMOUNT, BOD_BUY_PRICE, BOD_TOTAL_PRICE,
BO_ID, BOD_IMEI_LIST)
<foreach close=")" collection="list" item="item" index="index" open="(" separator="union">
SELECT
#{item.bodId,jdbcType=VARCHAR}, #{item.goodsId,jdbcType=VARCHAR},
#{item.goodsName,jdbcType=VARCHAR},#{item.goodsUnit,jdbcType=VARCHAR},
#{item.goodsType,jdbcType=VARCHAR}, #{item.goodsColor,jdbcType=VARCHAR},
#{item.bodAmount,jdbcType=DECIMAL}, #{item.bodBuyPrice,jdbcType=DECIMAL},
#{item.bodTotalPrice,jdbcType=DECIMAL}, #{item.boId,jdbcType=VARCHAR},
#{item.bodImeiList,jdbcType=CLOB}
FROM DUAL
</foreach>
</insert>
<!-- 分页查询商品,动态sql -->
<select id="selectPageListUseDys" parameterType="goods" resultMap="goodsResultMap">
SELECT * FROM (
SELECT ROWNUM RN,GO.* FROM
(SELECT * FROM GOODS
<where>
<if test="paramEntity.goodsName!=null">AND GOODS_NAME LIKE #{paramEntity.goodsName}</if>
</where>
ORDER BY GOODS_ID)GO
WHERE ROWNUM <= #{start}+#{rows})
WHERE RN > #{start}
</select>
错误例2:将整个sql语句用<![CDATA[ ]]>
标记来避免冲突,在一般情况下都是可行的,是由于该sql配置中有动态语句(where部分),将导致系统无法识别动态判断部分,导致整个sql语句非法。
<select id="find" parameterClass="java.util.Map" resultClass="java.lang.Long"
<![CDATA[ select id from tableA a,tableB b <dynamic prepend="WHERE"> <isNotNull prepend="AND" property="startDate"
a.act_time >= #startDate# and a.act_time <= #endDate# and a.id = b.id </isNotNull>
</dynamic> ]]> </select> 正确做法:缩小范围,只对有字符冲突部分进行合法性调整。
<select id="find" parameterClass="java.util.Map" resultClass="java.lang.Long"> select i
from tableA a, tableB b <dynamic prepend="WHERE"> <isNotNull prepend="AND" property="startDate">
a.act_time >= #startDate# <![CDATA[ and a.act_time <= #endDate# ]]> and a.id = b.id
</isNotNull> </dynamic> </select>
ibatis中应该经常见到<![CDATA[这样的东西吧,它的用处应该是转义一些特殊关键字字符,
不合法的XML字符必须被替换为相应的实体。 下面是五个在XML文档中预定义好的实体:
< < 小于号
> > 大于号
& & 和
' ' 单引号
" " 双引号