spring mybatis整合

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

首先开启注解扫描

<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 &lt;= #{start}+#{rows})
WHERE RN &gt; #{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文档中预定义好的实体: 
&lt;                 <       小于号                                            
&gt;                 >       大于号 
&amp;                &       和 
&apos;               '       单引号 
&quot;               "       双引号 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容