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七大模块详解
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
首先开启注解扫描
然后开启aop自动实现代理
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
首先开启注解扫描
然后开启aop自动实现代理
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切面类:
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);
}
pring声明式事务管理
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 threadLocal = new ThreadLocal();
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();
}
}
}
映射配置
insert into students values(1,'张三',7000)
insert into students values(#{id},#{name},#{sal})
update students set name=#{name},sal=#{sal} where id=#{id}
delete from students where id=#{id}
update students
name=#{name},
sal=#{sal}
where id=#{id}
delete from students where id in
#{ids}
delete from students where id in
#{list}
insert into students (id,name,sal)
values(#{id},#{name},#{sal})
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();
// 插入对象,指定映射空间名称,和标签的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 findAll() throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List studentList = new ArrayList();
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 findPage(int start, int end) throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List students = new ArrayList();
try {
Map map = new LinkedHashMap();
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 dynaSelect(Integer id, String name, Double sal)
throws Exception {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List list = new ArrayList();
try {
Map map = new LinkedHashMap();
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 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配置文件
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
sshProject2
struts2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts2
/*
contextConfigLocation
classpath:bean.xml
org.springframework.web.context.ContextLoaderListener
index.jsp
struts.xml
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 listDept = deptService.getAll();
// 4.数据回显
// 获取valueStack对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
// 移除栈顶元素(旧的)
valueStack.pop();
// 入栈,即将更新的值
valueStack.push(employee);
request.put("listDept", listDept);
return "edit";
}
下拉列表
做注册的时候员工的信息联系到另外一张表部门表,下拉菜单选择部门的时候这个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 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)
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
错误例2:将整个sql语句用
标记来避免冲突,在一般情况下都是可行的,是由于该sql配置中有动态语句(where部分),将导致系统无法识别动态判断部分,导致整个sql语句非法。
正确做法:缩小范围,只对有字符冲突部分进行合法性调整。
ibatis中应该经常见到 大于号
& & 和
' ' 单引号
" " 双引号