第五章 MyBatis关联关系查询 - 多对多

5.1 多对多关联关系

一个学生可以选多门课程,而一门课程可以由多个学生选。这就是典型的多对多关系。所谓多对多,其实是由两个互反的一对多关系组成。

一般情况下,多对多关系都会通过一个中间表来建立。course表,middle表,student表。

定义实体类:Course.java

package com.bjpowernode.beans;

public class Course {
    private Integer cid;
    private String cname;
    private Set<Student> students;
    public Integer getCid() {
        return cid;
    }
    ...
    getter and setter methods
    ...
}

定义实体类:Student.java

package com.bjpowernode.beans;

public class Student {
    private Integer sid;
    private String sname;
    private Set<Course> courses;
    public Integer getSid() {
        return sid;
    }
    ...
    getter and setter methods
    ...        
}

接口

package com.bjpowernode.dao;

public interface IStudentDao {
    Student selectStudentById(int sid);
}

映射文件

<?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.bjpowernode.dao.IStudentDao">

    <resultMap type="Student" id="studentlMapper">
        <id column="sid" property="sid"/>
        <result column="sname" property="sname"/>
        <collection property="courses" ofType="Course">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
        </collection>
    </resultMap>

    <select id="selectStudentById" resultMap="studentlMapper">
        select sid, sname, cid, cname 
        from student, middle, course
        where sid=studentId and cid=courseId and sid=#{xxx}
    </select>

</mapper>

测试类

package com.bjpowernode.test;

public class MyTest {

    private IStudentDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(IStudentDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void test01() {
        Student student = dao.selectStudentById(1);
        System.out.println(student);
    }
}

第四章 MyBatis关联关系查询 - 自关联

4.1 自关联查询

所谓自关联查询是指,自己既充当一方,又充当多方。例如,对于新闻栏目NewsLabel表,既有充当一方的父栏目,也有充当多方的子栏目。而反映到DB表中,只有一张表,这张表中具有一个外键pid,用于表示该栏目的父栏目。一级栏目没有父栏目,其外键值pid为0,而子栏目则具有外键值。

定义DB表 newslabel :

  • id,name,pid
  • 1, 新闻娱乐,0
  • 2, 体育新闻,0
  • 3, NBA
  • 4, CBA
  • 5, 火箭, 3
  • 6, 湖人, 3
  • 7, 北京金隅, 4
  • 8, 浙江广厦, 4
  • 9, 青岛双星, 4
  • 10,港台明星, 1
  • 11,内地影展, 1

4.2 自关联查询 - 以一对多的方式实现 - 查询指定栏目的所有子孙栏目

定义实体类:NewsLabel.java

package com.bjpowernode.beans;

import java.util.Set;

//新闻栏目:当前的新闻栏目被看作是一方,即父栏目
public class NewsLabel {
    private Integer id;
    private String name;
    //定义关联属性子栏目
    private Set<NewsLabel> children;
    ...
    getter and setter methods
    ...
}

接口

package com.bjpowernode.dao;

public interface INewsLabelDao {
    List<NewsLabel> selectChildrenByParent(int pid);
}

映射文件

<?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.bjpowernode.dao.INewsLabelDao">

    <!-- <select id="">
        select id,name from newslabel where pid=#{ooo}
    </select> -->

    <resultMap type="NewsLabel" id="newsLabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <collection property="children"
                    ofType="NewsLabel"
                    select="selectChildrenByParent"
                    column="id"
        />
    </resultMap>

    <select id="selectChildrenByParent" resultMap="newsLabelMapper">
        select id,name from newslabel where pid=#{xxx}
    </select>

</mapper>

测试类

package com.bjpowernode.test;

public class MyTest {

    private INewsLabelDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(INewsLabelDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void test01() {
        List<NewsLabel> children = dao.selectChildrenByParent(2);
        for(NewsLabel newsLabel:children) {
            System.out.println(newsLabel);
        }
    }
}

4.3 自关联查询 - 以一对多的方式实现 - 查询指定栏目及其所有子孙栏目

实体类不变

接口

import com.bjpowernode.beans.NewsLabel;

public interface INewsLabelDao {
    NewsLabel selectNewsLabelById(int id);
}

映射文件

<?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.bjpowernode.dao.INewsLabelDao">

    <select id="selectNewsLabelByParent" resultMap="newsLabelMapper">
        select id,name from newslabel where pid = #{ooo}
    </select>

    <resultMap type="NewsLabel" id="newsLabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <collection property="children"
                    ofType="NewsLabel"
                    select="selectNewsLabelByParent"
                    column="id"
        />
    </resultMap>

    <select id="selectNewsLabelById" resultMap="newsLabelMapper">
        select id,name from newslabel where id=#{xxx}
    </select>

</mapper>

4.3 自关联查询 - 以多对一的方式实现 - 查询指定栏目及其父栏目

定义实体类

package com.bjpowernode.beans;

import java.util.Set;

//新闻栏目:当前的新闻栏目被看作是多方,即子栏目
public class NewsLabel {
    private Integer id;
    private String name;
    //定义关联属性父栏目
    private NewsLabel parent;
    ...
    getter and setter methods
    ...
}

定义接口

package com.bjpowernode.dao;

public interface INewsLabelDao {
    NewsLabel selectNewsLabelById(int id);
}

映射文件

<?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.bjpowernode.dao.INewsLabelDao">
    <!-- 
    <select id="" resultMap="">
        select id,name,pid from newslabel where id = #{ooo}
    </select>
    -->
    <resultMap type="NewsLabel" id="newsLabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <association property="parent" 
                     javaType="NewsLabel"
                     select="selectNewsLabelById"
                     column="pid"
        />
    </resultMap>

    <select id="selectNewsLabelById" resultMap="newsLabelMapper">
        select id,name,pid from newslabel where id=#{xxx}
    </select>

</mapper>

测试类

package com.bjpowernode.test;

public class MyTest {

    private INewsLabelDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(INewsLabelDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }


    @Test
    public void test01() {
        NewsLabel newsLabel = dao.selectNewsLabelById(7);
        System.out.println(newsLabel);
    }
}

第三章 MyBatis关联关系查询 - 多对一

3.1 多对一关联查询 - 通过多表连接查询方式实现

以部长Minister与国家Countr与间的多对一关系而言,查询部长时,将这个部长所属的国家查询出来。

DB数据模型不变,还是在多表minister中定义外键。

定义实体类:Country.java

package com.bjpowernode.beans;

public class Country {
    private Integer cid;
    private String cname;

    public Country() {
        super();
    }

    ...
    getter and setter methods
    ...        
}

定义实体类:Minister.java

package com.bjpowernode.beans;

public class Minister {
    private Integer mid;
    private String mname;
    //关联属性
    private Country country;

    public Minister() {
        super();
    }

    public Minister(Integer mid, String mname) {
        super();
        this.mid = mid;
        this.mname = mname;
    }

    ...
    getter and setter methods
    ...    
}

接口

package com.bjpowernode.dao;

public interface IMinisterDao {
    Minister selectMinisterById(int mid);
}

映射文件:

<?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.bjpowernode.dao.IMinisterDao">

    <resultMap type="Minister" id="ministerMapper">
        <id column="mid" property="mid"/>
        <result column="mname" property="mname"/>
        <association property="country" javaType="Country">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
        </association>
    </resultMap>

    <select id="selectMinisterById" resultMap="ministerMapper">
        select mid,mname,cid,cname
        from minister,country
        where countryId=cid and mid=#{ooo}
    </select>

</mapper>

测试类:

package com.bjpowernode.test;

public class MyTest {

    private IMinisterDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(IMinisterDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void test01() {
        Minister minister = dao.selectMinisterById(2);
        System.out.println(minister);
    }
}

3.2 多对一关联查询 - 通过多表单独查询方式实现

映射文件:

<?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.bjpowernode.dao.IMinisterDao">

    <select id="selectCountryByCountryId" resultType="Country">
        select cid,cname from country where cid=#{ooo}
    </select>

    <resultMap type="Minister" id="ministerMapper">
        <id column="mid" property="mid"/>
        <result column="mname" property="mname"/>
        <association 
            property="country" 
            javaType="Country" 
            select="selectCountryByCountryId"
            column="countryId"
        />

    </resultMap>

    <select id="selectMinisterById" resultMap="ministerMapper">
        select mid,mname,countryId from minister where mid=#{ooo}
    </select>

</mapper>

第二章 MyBatis关联关系查询 - 一对多

2.1 关联查询

当查询的内容涉及到具有关联关系的多个表时,就需要使用关联查询。根据表与表之间的关联关系的不同,关联查询分为四种:

(1)一对一关联查询

(2)一对多关联查询

(3)多对一关联查询

(4)多对多关联查询

日常工作中最常见的关联关系是一对多、多对一与多对多,一对一的解决方案与多对一的解决方案是相同的。

外键定义在多方表中,反过来说,如果一个表里面有外键,那么和某一个其它表相比,它肯定充当着多方。

定义DB

  • country: cid, cname

  • minister: mid, mname, countryId

2.2 一对多关联查询 - 通过多表连接查询方式实现

这里的一对多关联查询是指,在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。

以国家Country与部长Minister间的一对多关系而言,查询某一个国家时,将这个国家所有的部长查询出来。

定义实体类:Country.java

package com.bjpowernode.beans;

import java.util.Set;

public class Country {
    private Integer cid;
    private String cname;
    //关联属性
    private Set<Minister> ministers;

    public Country() {
        super();
    }

    ...
    getter and setter methods
    ...

}

定义实体类:Minister.java

package com.bjpowernode.beans;

public class Minister {
    private Integer mid;
    private String mname;

    public Minister() {
        super();
    }

    ...
    getter and setter methods
    ...

}

接口:ICountryDao

package com.bjpowernode.dao;

public interface ICountryDao {
    Country selectCountryById(int cid);
}

测试类:

package com.bjpowernode.test;

public class MyTest {

    private ICountryDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(ICountryDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void test01() {
        Country country = dao.selectCountryById(2);
        System.out.println(country);
    }
}

映射文件:

<?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.bjpowernode.dao.ICountryDao">

    <resultMap type="Country" id="countryMapper">
        <id column="cid" property="cid"/>
        <result column="cname" property="cname"/>
        <collection property="ministers" ofType="Minister">
            <id column="mid" property="mid"/>
            <result column="mname" property="mname"/>
        </collection>
    </resultMap>

    <select id="selectCountryById" resultMap="countryMapper">
        select cid,cname,mid,mname
        from country,minister
        where countryId=cid and cid=#{xxx}
    </select>

</mapper>

2.3 一对多关联查询 - 通过多表单独查询方式实现

映射文件

<?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.bjpowernode.dao.ICountryDao">

    <!-- 这使用种方式比多表联合查询方式多,因为这种方式可以使用延迟加载 -->

    <select id="selectMinisterByCountry" resultType="Minister">
        select mid,mname from minister where countryId=#{000}
    </select>

    <resultMap type="Country" id="countryMapper">
        <id column="cid" property="cid"/>
        <result column="cname" property="cname"/>
        <collection property="ministers" 
                    ofType="Minister" 
                    select="selectMinisterByCountry"
                    column="cid"
        />
    </resultMap>

    <select id="selectCountryById" resultMap="countryMapper">
        select cid,cname from country where cid=#{xxx}
    </select>

</mapper>

第一章 MyBatis单表的CURD操作

1.1 简介

MyBatis是一个优秀的基于java的持久层框架,它内部封装了JDBC,使开发者只需关注SQL语句本身,不用再花费精力去处理诸如注册驱动、创建Connection、配置Statement等繁杂过程。

MyBatis通过xml或注解的方式将要执行的各种statement配置起来,并通过Java对象和Statement中的SQL动态参数进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射成Java对象并返回。

1.2 MyBatis与Hibernate

Hibernate框架是提供了全面的封装机制的“全自动”ORM,即实现了POJO和数据库表之间的映射,以及SQL的自动生成和执行。

相对于此,MyBatis只能算作是“半自动”ORM。其着力点,是在POJO与SQL语句之间的映射。也就是说,MyBatis并不会为程序员自动生成SQL语句。具体的SQL需要程序员自己编写,然后通过SQL语句映射文件,将SQL所需要的参数,以及返回的结果字段映射到指定的POJO。因此MyBatis成为了“全自动”ORM的一种有益补充。

与Hibernate相比,MyBaits具有以下几个特点:

(1)、在XML文件中配置SQL语句,实现了SQL语句与代码的分离,给程序的维护带来了很大便利。

(2)、因为需要程序员自己编写SQL语句,程序员可以结合数据库自身的特点灵活控制SQL语句,因此能够实现比Hibernate等全自动ORM框架更高的查询效率,能够完成复杂查询。

(3)、简单、易于学习,易于使用,上手快。

1.3 Mapper动态代理

Mybatis框架抛开了Dao的实现类,直接定位到映射文件mapper中的相应SQL语句,对DB进行操作。这种对Dao的实现方式称为Mapper的动态代理方式。

Mapper动态代理方式无需程序员实现Dao接口。接口是由MyBatis结合映射文件自动生成的动态代理实现的。

动态代理的实现:

  • 将mapper的namespace设为接口的全限制性类名

  • 将mapper的sql语句的id设为接口的方法名

Dao的获取:

SqlSession sqlSession = MyBatisUtils.getSqlSession();
studentDao = sqlSession.getMappper(IStudentDao.class);

1.4 CURD 单表操作

POJO: Student.java

package com.bjpowernode.beans;

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

}

接口:IStudentDao.java

package com.bjpowernode.dao;

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

import com.bjpowernode.beans.Student;

public interface IStudentDao {
    //插入
    void insertStudent(Student student);
    //插入后获取id
    void insertStudentCatchId(Student student);
    //删除
    void deleteStudentById(int id);
    //修改
    void updateStudent(Student student);
    //查询所有学生对象
    List<Student> selectAllStudents();

    //根据id查询某个学生
    Student selectStudentById(int id);

    //根据名字模糊查询
    List<Student> selectStudentsByName(String name);
}

工具类:

package com.bjpowernode.utils;

public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory;

    public static SqlSession getSqlSession() {
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
            if(sqlSessionFactory == null) {
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            }
            return sqlSessionFactory.openSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}    

数据库连接四要素配置文件:jdbc_mysql.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mydatabase
jdbc.user=root
jdbc.password=haojie_123456

主配置文件:mybatis.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>
    <!-- 注册DB连接四要素属性文件 -->
    <properties resource="jdbc_mysql.properties"></properties>

    <!-- 设置别名 -->
    <typeAliases>
        <!-- 将指定包中所有类的简单类名当作别名 -->
        <package name="com.bjpowernode.beans"/>
    </typeAliases>

    <!-- 配置运行环境 -->
    <environments default="mysqlEM">
        <environment id="mysqlEM">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 注册映射文件 -->
    <mappers>
        <mapper resource="com/bjpowernode/dao/mapper.xml" />    
    </mappers>

</configuration>

映射文件:mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.bjpowernode.dao.IStudentDao">

    <insert id="insertStudent" parameterType="Student">
        insert into student(name,age,score) values(#{name},#{age},#{score})
    </insert>

    <insert id="insertStudentCatchId">
        insert into student(name,age,score) values(#{name},#{age},#{score})
        <selectKey resultType="int" keyProperty="id" order="AFTER">
            select @@identity
        </selectKey>
    </insert>

    <delete id="deleteStudentById">
        delete from student where id=#{aaa} <!-- 这里的#{}仅仅是个占位符,里面放什么都行 -->
    </delete>

    <update id="updateStudent">
        update student set name=#{name}, age=#{age}, score=#{score} where id=#{id}
    </update>

    <select id="selectAllStudents" resultType="Student">
        select id,name,age,score from student
    </select>

    <!-- 
        resultType: 表示通过反映机制,调用set+属性名的方式,将结果封装为Student对象
        如果属性名和字段名不一致,表中的字段为tid,tname,tage,score,属性名为id,name,age,score
        解决方式一:字段别名 select tid id, tname name, tage age, score frome student
        解决方式二:resultMap 
            <resultMap type="Student" id="studentMapper">
                <id column="tid" property="id" />
                <result column="tname" property="name" />
                <result column="tage" property="age" />
            </resultMap>
            <select id="selectAllStudents" resultMap="studentMapper">
                select id,name,age,score from student
            </select>        
     -->    

    <select id="selectStudentById" resultType="Student">
        select id,name,age,score from student where id=#{bbbbbb}
    </select>

    <select id="selectStudentsByName" resultType="Student">
        <!-- select id,name,age,score from student where name like concat('%',#{xxx},'%') -->
        select id,name,age,score from student where name like '%' #{xxx} '%'
    </select>

</mapper>

测试文件:Mytest.java

package com.bjpowernode.test;

public class MyTest {

    private IStudentDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(IStudentDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void testInsertStudent() {
        Student student = new Student("zhangsan2", 24, 1);
        dao.insertStudent(student);
        sqlSession.commit();
    }

    @Test
    public void insertStudentCatchId() {
        Student student = new Student("lisi", 24, 94);
        System.out.println("插入前:student="+student);
        dao.insertStudentCatchId(student);
        System.out.println("插入后:student="+student);
    }

    @Test
    public void testDeleteStudentById() {
        dao.deleteStudentById(13);
    }

    @Test
    public void testUpdateStudent() {
        Student student = new Student("wangwu",25,95);
        student.setId(14);
        dao.updateStudent(student);
    }

    @Test
    public void testSelectAllStudents() {
        List<Student> students = dao.selectAllStudents();
        for(Student student:students) {
            System.out.println(student);
        }
    }

    @Test
    public void testSelectStudentById() {
        Student student = dao.selectStudentById(14);
        System.out.println(student);
    }

    @Test
    public void testSelectStudentByName() {
        List<Student> students = dao.selectStudentsByName("zhang");
        for(Student student:students) {
            System.out.println(student);
        }
    }
}

1.5 多条件查询问题 - 根据Map查询

接口:IStudentDao.java

package com.bjpowernode.dao;

public interface IStudentDao {    
    List<Student> selectStudentsByCondition(Map<String, Object> map);
}

映射文件:

<?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.bjpowernode.dao.IStudentDao">


    <select id="selectStudentsByCondition" resultType="Student">
        <!-- #{}里面可以放对象的属性名,也可以放map的key,也可以直接通过对象获取属性:如student.score -->
        select id,name,age,score from student where name like '%' #{nameCondition} '%' and age > #{ageCondition}
    </select>

</mapper>

测试类:MyTest.java

package com.bjpowernode.test;

public class MyTest {

    private IStudentDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(IStudentDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void testSelectStudentsByCondition() {
         Map<String, Object> map = new HashMap<String, Object>();
         map.put("nameCondition", "zhang");
         map.put("ageCondition", 10);

         List<Student> students = dao.selectStudentsByCondition(map);
         for(Student student:students) {
             System.out.println(student);
         }
    }
}

1.6 多条件查询问题 - 使用索引号

接口类

package com.bjpowernode.dao;

public interface IStudentDao {    
    List<Student> selectStudentsByCondition(String name, int age);
}

映射文件:

<?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.bjpowernode.dao.IStudentDao">


    <select id="selectStudentsByCondition" resultType="Student">
        <!-- 
            #{}里面可以放:
            1、对象的属性名,
            2、参数为map时,可以放map的key,
            3、参数为map时,或key所对应的value为对象,则可以放入对象的属性如:Student.score 
            4、也可以放索引
            5、当#{}是个占位符时,可以放随意字符
        -->
        select id,name,age,score 
        from student 
        where name like '%' #{0} '%' 
        and age > #{1}
    </select>

</mapper>

测试类

package com.bjpowernode.test;

public class MyTest {

    private IStudentDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(IStudentDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }


    @Test
    public void testSelectStudentsByCondition() {

         List<Student> students = dao.selectStudentsByCondition("zhang",23);
         for(Student student:students) {
             System.out.println(student);
         }
    }
}

1.7 动态SQL

动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的SQL语句不同。若将每种情况均逐一列出,对所有条件进行排列组合,将会出现大理的SQL语句。此时,可使用动态SQL来解决这样的问题。

动态SQL - if、where、choose、foreach、foreach-list、foreach-泛型自定义类型list

测试类:

package com.bjpowernode.test;

public class MyTest {

    private IStudentDao dao;
    private SqlSession sqlSession;

    @Before
    public void Before() {
        sqlSession = MyBatisUtils.getSqlSession();
        //dao:是一个动态代理
        dao = sqlSession.getMapper(IStudentDao.class);
    }

    @After
    public void after() {
        if(sqlSession != null) {
            sqlSession.close();
        }
    }

    @Test
    public void testSelectStudentsByIf() {
        Student student = new Student("zhang",23,0);
         List<Student> students = dao.selectStudentsByIf(student);
         for(Student student1:students) {
             System.out.println(student1);
         }
    }

    @Test
    public void testSelectStudentsByWhere() {
        Student student = new Student("",0,0);
         List<Student> students = dao.selectStudentsByWhere(student);
         for(Student student1:students) {
             System.out.println(student1);
         }
    }

    @Test
    public void testSelectStudentsByChoose() {
        Student student = new Student("",0,0);
         List<Student> students = dao.selectStudentsByChoose(student);
         for(Student student1:students) {
             System.out.println(student1);
         }
    }

    @Test
    public void testForeach() {
        int[] ids = {14,16,18};
        List<Student> students = dao.selectStudentsByForeach(ids);
        for(Student student1:students) {
            System.out.println(student1);
        }
    }

    @Test
    public void testForeachList() {
        List<Integer> ids = new ArrayList<>();
        ids.add(14);
        ids.add(16);
        ids.add(18);
        List<Student> students = dao.selectStudentsByForeachList(ids);
        for(Student student1:students) {
            System.out.println(student1);
        }
    }

    @Test
    public void testForeachObject() {
        Student stu1 = new Student();
        stu1.setId(14);

        Student stu2 = new Student();
        stu2.setId(16);

        List<Student> stus = new ArrayList<>();
        stus.add(stu1);
        stus.add(stu2);

        List<Student> students = dao.selectStudentsByForeachObject(stus);
        for(Student student1:students) {
            System.out.println(student1);
        }
    }
}

接口

package com.bjpowernode.dao;

public interface IStudentDao {
    List<Student> selectStudentsByIf(Student student);

    List<Student> selectStudentsByWhere(Student student);

    List<Student> selectStudentsByChoose(Student student);

    List<Student> selectStudentsByForeach(int[] ids);

    List<Student> selectStudentsByForeachList(List<Integer> ids);

    List<Student> selectStudentsByForeachObject(List<Student> students);

}

映射文件

<?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.bjpowernode.dao.IStudentDao">

    <select id="selectStudentsByIf" resultType="Student">
        select id,name,age,score 
        from student 
        where 1 = 1 
        <if test="name != null and name !=''">
            and name like '%' #{name} '%'
        </if>
        <if test="age > 0">
            and age > #{age}
        </if>
    </select>

    <select id="selectStudentsByWhere" resultType="Student">
        select id,name,age,score 
        from student 
        <where>
            <if test="name != null and name !=''">
                and name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                and age > #{age}
            </if>
        </where>
    </select>

    <!-- 
        若姓名不空,则按姓名查询;若年龄不空,则按年龄查询;都为空,不查询。
     -->
    <select id="selectStudentsByChoose" resultType="Student">
        select id,name,age,score 
        from student 
        <where>
            <choose>
                <when test="name != null and name != ''">
                    and name like '%' #{name} '%'
                </when>
                <when test="age > 0">
                    and age > #{age}
                </when>
                <otherwise>
                    1 > 2
                </otherwise>
            </choose>
        </where>
    </select>

    <select id="selectStudentsByForeach" resultType="Student">
        <!-- select id,name,age,score from student where id in (1,3,5) -->
        select id,name,age,score 
        from student 
        <if test="array.length > 0">
            where id in 
            <foreach collection="array" item="myid" open="(" close=")" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

    <select id="selectStudentsByForeachList" resultType="Student">
        <!-- select id,name,age,score from student where id in (1,3,5) -->
        select id,name,age,score 
        from student 
        <if test="list.size > 0">
            where id in 
            <foreach collection="list" item="myid" open="(" close=")" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

    <select id="selectStudentsByForeachObject" resultType="Student">
        select id,name,age,score 
        from student 
        <if test="list.size > 0">
            where id in 
            <foreach collection="list" item="stu" open="(" close=")" separator=",">
                #{stu.id}
            </foreach>
        </if>
    </select>

    <sql id="">

    </sql>

</mapper>    

第十章 Spring事务管理

10.1 事务管理器接口

事务管理器是PlatformTransactionManager接口对象。其主要用于完成事务的提交、回滚及获取事务的状态信息。

常用的两个实现类
  • DataSourceTransactionManager:使用JDBC或iBatis进行持久化数据时使用。

  • HibernateTransactionManager:使用Hibernate进行持久化数据时使用。

Spring的回滚方式

Spring事务的默认回滚方式是:发生运行异常时回滚,发生受查异常时提交。

运行异常指的是继承自RuntimeException的异常类,受查异常指的是继承自Exception的异常类。

10.2 事务定义接口

TransactionDefinition

隔离级别:
  • static int ISOLATION_DEFAULT 0

  • static int ISOLATION_READ_COMMITTED 2

  • static int ISOLATION_READ_UNCOMMITED 1

  • static int ISOLATION_REPEATABLE_READ 4

  • static int ISOLATION_SERIALIZABLE 8

MYSQL默认的是可重复读,ORACLE默认的是读已提交

事务的传播行为

定义了七个事务传播行为。

doSome()方法调用doOther()方法,或doSome()方法存在于事务中,则doOther()方法加入到该事务中执行;若doSome()方法在调用doOther()方法时没有在事务内执行,则doOther()方法会创建一个事务,并在其中执行。

10.3 程序举例

两个实体:银行账户Account与股票账户Stock。当要购买股票时,从Account中扣除相应金额的存款,同时在Stock中增加相应的股票数量。在这个过程中,抛出一个用户自定义的异常,使两个操作回滚。

10.4 定义实体及DB表

Account.java

package com.bjpowernode.beans;

public class Account {
    private Integer aid;
    private String aname;
    private double balance;
    public Account() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Account(String aname, double balance) {
        super();
        this.aname = aname;
        this.balance = balance;
    }
    public Integer getAid() {
        return aid;
    }
    public void setAid(Integer aid) {
        this.aid = aid;
    }
    public String getAname() {
        return aname;
    }
    public void setAname(String aname) {
        this.aname = aname;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    @Override
    public String toString() {
        return "Account [aid=" + aid + ", aname=" + aname + ", balance=" + balance + "]";
    }

}

Stock.java

package com.bjpowernode.beans;

public class Stock {
    private Integer sid;
    private String sname;
    private int count;

    public Stock() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Stock(String sname, int count) {
        super();
        this.sname = sname;
        this.count = count;
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public String toString() {
        return "Stock [sid=" + sid + ", sname=" + sname + ", count=" + count + "]";
    }

}

10.5 定义service

业务接口

package com.bjpowernode.service;

public interface IBuyStockService {
    void openAccount(String aname, double money);
    void openStock(String sname, int amount);
    void buyStock(String aname, double money, String sname, int amount) throws BuyStockException;
}

业务实现类

package com.bjpowernode.service;

import com.bjpowernode.Dao.IAccountDao;
import com.bjpowernode.Dao.IStockDao;

public class BuyStockServiceImpl implements IBuyStockService {
    private IAccountDao adao;
    private IStockDao sdao;

    public void setAdao(IAccountDao adao) {
        this.adao = adao;
    }

    public void setSdao(IStockDao sdao) {
        this.sdao = sdao;
    }

    @Override
    public void openAccount(String aname, double money) {
        adao.insertAccount(aname, money);
    }

    @Override
    public void openStock(String sname, int amount) {
        sdao.insertStock(sname, amount);
    }

    @Override
    public void buyStock(String aname, double money, String sname, int amount) throws BuyStockException {
        boolean isBuy = true;
        adao.updateAccount(aname, money, isBuy);
        if(true) {
            throw new BuyStockException("购买股票异常");
        }
        sdao.updateStock(sname, amount, isBuy);
    }

}

10.6 定义Dao

IAccountDao

package com.bjpowernode.Dao;

public interface IAccountDao {

    void insertAccount(String aname, double money);

    void updateAccount(String aname, double money, boolean isBuy);

}

AccountDaoImpl

package com.bjpowernode.Dao;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    @Override
    public void insertAccount(String aname, double money) {
        String sql = "insert into account(aname, balance) values(?,?)";
        this.getJdbcTemplate().update(sql, aname, money);
    }

    @Override
    public void updateAccount(String aname, double money, boolean isBuy) {
        String sql = "update account set balance = balance + ? where aname=?";
        if(isBuy) {
            sql = "update account set balance = balance - ? where aname=?";
        }
        this.getJdbcTemplate().update(sql, money, aname);
    }

}

IStockDao

package com.bjpowernode.Dao;

public interface IStockDao {

    void insertStock(String sname, int amount);

    void updateStock(String sname, int amount, boolean isBuy);

}

StockDaoImpl

package com.bjpowernode.Dao;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class StockDaoImpl extends JdbcDaoSupport implements IStockDao {

    @Override
    public void insertStock(String sname, int amount) {
        String sql = "insert into stock(sname, count) values(?,?)";
        this.getJdbcTemplate().update(sql, sname, amount);
    }

    @Override
    public void updateStock(String sname, int amount, boolean isBuy) {
        String sql = "update stock set count = count-? where sname=?";
        if(isBuy) {
            sql = "update stock set count = count+? where sname=?";            
        }
        this.getJdbcTemplate().update(sql, amount, sname);
    }

}

10.7 定义测试类

MyTest.java

package com.bjpowernode.test;

import java.nio.channels.AcceptPendingException;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.springframework.cache.support.AbstractCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.support.DaoSupport;

import com.bjpowernode.service.BuyStockException;
import com.bjpowernode.service.IBuyStockService;


public class MyTest {

    private IBuyStockService service;

    @Before
    public void before() {
        String source = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(source);
        service = (IBuyStockService) ac.getBean("buyStockService");
    }

    @Test
    public void test01() {
        service.openAccount("张三", 10000);
        service.openStock("动力节点", 0);
    }    

    @Test
    public void test02() {
        try {
            service.buyStock("张三", 2000, "动力节点", 5);
        } catch (BuyStockException e) {
            e.printStackTrace();
        }
    }    

}

10.8 异常类

package com.bjpowernode.service;

public class BuyStockException extends Exception {

    public BuyStockException() {
        super();
    }

    public BuyStockException(String message) {
        super(message);
    }

}

10.9 注册

applicationContext.xml

<?xml version="1.0" encoding="utf-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.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.xsd">

    <!-- 注册属性文件:从属性文件读取DB连接四要素:方式二 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- 注册数据源:Spring内置的连接池 -->
    <bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 注册dao -->
    <bean id="accountDao" class="com.bjpowernode.Dao.AccountDaoImpl">
        <property name="dataSource" ref="myDataSource"></property>
    </bean>

    <bean id="stockDao" class="com.bjpowernode.Dao.StockDaoImpl">
        <property name="dataSource" ref="myDataSource"></property>
    </bean>

    <!-- 注册service -->
    <bean id="buyStockService" class="com.bjpowernode.service.BuyStockServiceImpl">
        <property name="adao" ref="accountDao"></property>
        <property name="sdao" ref="stockDao"></property>
    </bean>

<!-- ============================ AOP ===================================================  -->

    <!-- 注册事务管理器 -->
    <bean id="myTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="myDataSource"></property>
    </bean>

    <!-- 注册事务通知 -->
    <tx:advice id="txAdvice" transaction-manager="myTransactionManager">
        <tx:attributes>
            <!-- 这里指定的是:为每一个连接点指定所要应用的事务属性 -->
            <tx:method name="open*" isolation="DEFAULT" propagation="REQUIRED"/>
            <tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback-for="BuyStockException"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!-- 这里指定的是切入点 -->
        <aop:pointcut expression="execution(* *..service.*.buyStock(..))" id="myPointcut"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>    
    </aop:config>

</beans>

第九章 spring与JDBC模板

为了避免直接使用JDBC而带来的复杂且冗长的代码,Spring提供了一个强有力的模板类-JdbcTemplate来简化JDBC操作。并且,数据源DataSource对象与模板JdbcTemplate对象均通过Bean的形式定义在配置文件中,充分发挥了依赖注入的威力。

9.1 导入jar包

spring-jdbc-*.jar

spring的事务 spring-tx-*.jar

c3p0: com.springsource.com.mchange.v2.c3p0-**.jar

dbcp:

  • com.springsource.org.apache.commons.dbcp-*.osgi.jar

  • com.springsource.org.apache.commons.pool-*.jar

mysql驱动:mysql-connector-java-*-bin.jar

9.3 定义实体类与DB表

student类

package com.bjpowernode.beans;

public class Student {
    private Integer id;
    private String name;
    private int age;

    public Student() {
        super();
    }


    public Student(Integer id, String name, int age) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
    }


    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }


    public void setId(Integer id) {
        this.id = id;
    }


    public Integer getId() {
        return id;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }

}

导出student.sql放入项目中

9.4 定义Service

IStudentService接口

package com.bjpowernode.service;

import java.util.List;

import com.bjpowernode.beans.Student;

public interface IStudentService {

    void addStudent(Student student);
    void removeById(int id);
    void modifyStudent(Student student);

    List<String> findAllStudentsNames();
    String findStudentNameById(int id);

    List<Student> findAllStudents();
    Student findStudentById(int id);

}

StudentServiceImpl实现类

package com.bjpowernode.service;

import java.util.List;

import com.bjpowernode.Dao.IStudentDao;
import com.bjpowernode.beans.Student;

public class StudentServiceImpl implements IStudentService {

    private IStudentDao dao;

    public void setDao(IStudentDao dao) {
        this.dao = dao;
    }


    @Override
    public void addStudent(Student student) {
        dao.insertStudent(student);
    }

    @Override
    public void removeById(int id) {
        dao.deleteById(id);
    }

    @Override
    public void modifyStudent(Student student) {
        dao.updateStudent(student);
    }

    @Override
    public List<String> findAllStudentsNames() {
        return dao.selectAllStudentsNames();
    }

    @Override
    public String findStudentNameById(int id) {
        return dao.selectStudentNameById(id);
    }

    @Override
    public List<Student> findAllStudents() {
        return dao.selectAllStudents();
    }

    @Override
    public Student findStudentById(int id) {
        return dao.selectStudentById(id);
    }    
}

9.5 定义Dao

IStudentDao

package com.bjpowernode.Dao;

import java.util.List;

import com.bjpowernode.beans.Student;

public interface IStudentDao {


    void insertStudent(Student student);
    void deleteById(int id);
    void updateStudent(Student student);

    List<String> selectAllStudentsNames();
    String selectStudentNameById(int id);

    List<Student> selectAllStudents();
    Student selectStudentById(int id); 
}

StudentDaoImpl

package com.bjpowernode.Dao;

import java.util.List;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

import com.bjpowernode.beans.Student;

//继承JdbcDaoSupport
public class StudentDaoImpl extends JdbcDaoSupport implements IStudentDao {

    //增
    @Override
    public void insertStudent(Student student) {
        String sql = "insert into student(name,age) values(?,?)";
        this.getJdbcTemplate().update(sql, student.getName(), student.getAge());
    }

    //删
    @Override
    public void deleteById(int id) {
        String sql = "delete from student where id=?";
        this.getJdbcTemplate().update(sql,id);
    }

    //改
    @Override
    public void updateStudent(Student student) {
        String sql = "update student set name=?, age=? where id=?";
        this.getJdbcTemplate().update(sql, student.getName(), student.getAge(), student.getId());
    }

    //查
    @Override
    public List<String> selectAllStudentsNames() {
        String sql = "select name from student";
        return this.getJdbcTemplate().queryForList(sql, String.class);
    }

    @Override
    public String selectStudentNameById(int id) {
        String sql = "select name from student where id=?";
        return this.getJdbcTemplate().queryForObject(sql, String.class, id);
    }

    @Override
    public List<Student> selectAllStudents() {
        String sql = "select id,name,age from student";
        //手动封装查询的数据集
        return this.getJdbcTemplate().query(sql, new StudentRowMapper());
    }

    @Override
    public Student selectStudentById(int id) {
        String sql = "select id,name,age from student where id=?";
        return this.getJdbcTemplate().queryForObject(sql, new StudentRowMapper(), id);
    }


}

9.6 定义测试类

MyTest.java

package com.bjpowernode.test;

public class MyTest {

    private IStudentService service;

    @Before
    public void before() {
        String source = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(source);
        service = (IStudentService) ac.getBean("studentService");
    }

    //增
    @Test
    public void test01() {
        Student student = new Student("王五2", 26);
        service.addStudent(student);
    }

    //删
    @Test
    public void test02() {
        service.removeById(2);
    }

    //改
    @Test
    public void test03() {
        Student student = new Student(1,"李四2",23);
        service.modifyStudent(student);
    }

    //查所有学生的名字
    @Test
    public void test04() {
        List<String> names = service.findAllStudentsNames();
        System.out.println(names);
    }

    //查某个学生的名字
    @Test
    public void test05() {
        String name = service.findStudentNameById(1);
        System.out.println(name);
    }

    //查所有学生
    @Test
    public void test06() {
        List<Student> students = service.findAllStudents();
        for(Student student:students) {
            System.out.println(student);
        }
    }

    //查某个学生
    @Test
    public void test07() {
        Student student = service.findStudentById(1);
        System.out.println(student);
    }

}

9.7 配置

jdbc.properties 连接四要素

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test
jdbc.user=root
jdbc.password=haojie_123456

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 注册数据源:Spring内置的连接池 -->
    <!-- <bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mydatabase"></property>
        <property name="username" value="root"></property>
        <property name="password" value="haojie_123456"></property>
    </bean> -->

    <!-- 注册数据源:DBCP -->
    <!-- <bean id="myDataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mydatabase"></property>
        <property name="username" value="root"></property>
        <property name="password" value="haojie_123456"></property>
    </bean> -->

    <!-- 注册数据源:C3P0 -->
    <!-- <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcurl" value="jdbc:mysql://127.0.0.1:3306/mydatabase"></property>
        <property name="user" value="root"></property>
        <property name="password" value="haojie_123456"></property>
    </bean> -->

    <!-- 注册属性文件:从属性文件读取DB连接四要素:方式一 -->
    <!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties"></property>
    </bean> -->

    <!-- 注册属性文件:从属性文件读取DB连接四要素:方式二 -->
    <!-- <context:property-placeholder location="classpath:jdbc.properties"/> -->

    <bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mydatabase"></property>
        <property name="username" value="root"></property>
        <property name="password" value="haojie_123456"></property>
    </bean>

    <!-- 将数据源注入给dao:方式一 -->

            <!-- 注册jdbctemplate -->
            <!-- <bean id="myJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
                <property name="dataSource" ref="myDataSource"></property>
            </bean> -->

            <!-- 注册dao -->
            <!-- <bean id="studentDao" class="com.bjpowernode.Dao.StudentDaoImpl">
                <property name="jdbcTemplate" ref="myJdbcTemplate"></property>
            </bean> -->

    <!-- 将数据源注入给dao:方式二 -->

            <!-- 注册dao -->
            <bean id="studentDao" class="com.bjpowernode.Dao.StudentDaoImpl">
                <property name="dataSource" ref="myDataSource"></property>
            </bean>


    <!-- 注册实现类 -->
    <bean id="studentService" class="com.bjpowernode.service.StudentServiceImpl">
        <property name="dao" ref="studentDao"></property>
    </bean>

</beans>

9.8 Jdbc模板的对象是多例的

JdbcTemplate对象是多例的,即系统会为每一个使用模板对象的线程(方法)创建一个JdbcTemplate实例,并且在该线程(方法)结束时,自动释放JdbcTemplate实例。所以在每次使用JdbcTemplate对象时,都需要通过getJdbcTemplate()方法获取。

第八章 Spring与AOP AspectJ基于XML的AOP实现

AspectJ基于XML的AOP实现

切面

//切面
public class MyAspect {

    public void myBefore() {
        System.out.println("执行前置方法");
    }

    public void myBefore(JoinPoint jp) {
        System.out.println("执行前置方法jp="+jp);
    }

    public void myAfterReturning(String result) {
        System.out.println("执行后置方法 result="+result);
    }

    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("执行环绕通知方法,目标方法执行之前");
        //目标方法
        Object result = pjp.proceed();
        System.out.println("执行环绕通知方法,目标方法执行之后");
        if(result != null) {
            result = ((String)result).toUpperCase();
        }
        return result;
    }

    public void myAfterThrowing(UsernameException ex) {
        System.out.println("发生用户名异常:"+ex.getMessage());
    }

    public void myAfterThrowing(PasswordException ex) {
        System.out.println("发生密码异常:"+ex.getMessage());
    }

    public void myAfterThrowing(Exception ex) {
        System.out.println("发生其它异常:"+ex.getMessage());
    }

    public void myAfter() {
        System.out.println("执行最终通知");
    }


}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd"> 

        <!-- 注册切面 -->
        <bean id="myAspect" class="com.siyantu.xml.MyAspect"></bean>

         <!-- 注册实现类 -->
        <bean id="someService" class="com.siyantu.xml.SomeServiceImpl" />

        <!-- AOP配置 -->
        <aop:config>
                <!-- 定义切入点 -->
                <aop:pointcut expression="execution(* *..ISomeService.doFirst(..))" id="doFirstPointcut"/>
                <aop:pointcut expression="execution(* *..ISomeService.doSecond(..))" id="doSecondPointcut"/>
                <aop:pointcut expression="execution(* *..ISomeService.doThird(..))" id="doThirdPointcut"/>

                <aop:aspect ref="myAspect">
                    <!-- 前置通知 -->
                    <aop:before method="myBefore" pointcut-ref="doFirstPointcut" />
                    <!-- 指定参数类型 -->
                    <aop:before method="myBefore(org.aspectj.lang.JoinPoint)" pointcut-ref="doFirstPointcut" />

                    <!-- 后置通知 -->
                    <aop:after-returning method="myAfterReturning" pointcut-ref="doSecondPointcut" returning="result" />

                    <!-- 环绕通知 -->
                    <aop:around method="myAround(org.aspectj.lang.ProceedingJoinPoint)" pointcut-ref="doSecondPointcut" />

                    <!-- 异常通知 -->
                    <aop:after-throwing method="myAfterThrowing(com.siyantu.xml.UsernameException)" pointcut-ref="doThirdPointcut" throwing="ex" />
                    <aop:after-throwing method="myAfterThrowing(com.siyantu.xml.PasswordException)" pointcut-ref="doThirdPointcut" throwing="ex" />
                    <aop:after-throwing method="myAfterThrowing(java.lang.Exception)" pointcut-ref="doThirdPointcut" throwing="ex" />

                    <!-- 最终通知 -->
                    <aop:after method="myAfter" pointcut-ref="doThirdPointcut" />

                </aop:aspect>

        </aop:config>

</beans>

第七章 Spring与AOP AspectJ基于注解的AOP实现

7.1 AspectJ 基于注解的AOP实现 - 前置通知

指定ISomeService的doFirst()接入点为切入点,并执行前置方法

主业务接口

public interface ISomeService {
    void doFirst();
    String doSecond(String str);
    void doThird();
}

业务实现类

public class SomeServiceImpl implements ISomeService {
    @Override
    public void doFirst() {
        System.out.println("执行doFirst()方法");
    }
    @Override
    public String doSecond(String str) {
        System.out.println("执行doSecond()方法");
        return str;
    }
    @Override
    public void doThird() {
        System.out.println("执行doThird()方法");
    }
}

切面

@Aspect //表示当前类为切面
public class MyAspect {
    //设置前置方法及切入点
    @Before("execution(* *..ISomeService.doFirst(..))")
    public void myBefore(JoinPoint jp) {
        System.out.println("执行前置方法jp="+jp);
    }
}

配置文件

<!-- 注册切面 -->
<bean id="myAspect" class="com.siyantu.annotation.MyAspect"></bean>

 <!-- 注册实现类 -->
<bean id="someService" class="com.siyantu.annotation.SomeServiceImpl" />

<!-- aspectj自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

测试类

public class MyTest {
    @Test
    public void test() {
        String resource = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        ISomeService service = (ISomeService) ac.getBean("someService");
        service.doFirst();
        System.out.println("=============");
        service.doSecond("abcde");
        System.out.println("=============");
        service.doThird();
        System.out.println("=============");

    }

}

7.2 AspectJ 基于注解的AOP实现 - 后置通知

@Aspect //表示当前类为切面
public class MyAspect {

    ...

    //设置后置方法及切入点,可以获取目标方法结果,但是因为没有返回值,所以没法修改
    @AfterReturning(value="execution(* *..ISomeService.doSecond(..))",returning="result")
    public void myAfterReturning(String result) {
        System.out.println("执行后置方法 result="+result);
    }

}

执行结果:

执行前置方法jp=execution(void com.siyantu.annotation.ISomeService.doFirst())
执行doFirst()方法
=============
执行doSecond()方法
执行后置方法 result=abcde
=============
执行doThird()方法
=============

7.3 AspectJ 基于注解的AOP实现 - 环绕通知

@Aspect //表示当前类为切面
public class MyAspect {

    ...
    //设置环绕通知及切入点
    @Around("execution(* *..ISomeService.doSecond(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("执行环绕通知方法,目标方法执行之前");
        //目标方法
        Object result = pjp.proceed();
        System.out.println("执行环绕通知方法,目标方法执行之后");
        if(result != null) {
            result = ((String)result).toUpperCase();
        }
        return result;
    }

}

执行结果:

执行前置方法jp=execution(void com.siyantu.annotation.ISomeService.doFirst())
执行doFirst()方法
=============
执行环绕通知方法,目标方法执行之前
执行doSecond()方法
执行环绕通知方法,目标方法执行之后
执行后置方法 result=ABCDE
=============
执行doThird()方法
=============

7.4 AspectJ 基于注解的AOP实现 - 异常通知

异常分两类:

1、运行时异常,不进行处理,也可以通过编译。

若一个类继承自RunTimeException,则该异常就是运行时异常。

2、编译时异常,受查异常,Checked Exception。不进行处理,将无法通过编译。

若一个类继承自Exception,则该类就是受查异常。

自定义异常

主业务接口

public interface ISomeService {
    void doThird(String username, String password) throws UserException;
}

业务实现类

public class SomeServiceImpl implements ISomeService {
    @Override
    public void doThird(String username, String password) throws UserException {
        if(!"beijing".equals(username)) {
            throw new UsernameException("用户名有误");
        }
        if(!"111".equals(password)) {
            throw new PasswordException("密码有误");
        }
    }
}

切面

@Aspect //表示当前类为切面
public class MyAspect {

    //当目标方法抛出与指定类型的异常具有is-a关系的异常时,执行该异常通知方法    
    @AfterThrowing(value="execution(* *..ISomeService.doThird(..))",throwing="ex")
    public void myAfterThrowing(UsernameException ex) {
        System.out.println("发生用户名异常:"+ex.getMessage());
    }

    @AfterThrowing(value="execution(* *..ISomeService.doThird(..))",throwing="ex")
    public void myAfterThrowing(PasswordException ex) {
        System.out.println("发生密码异常:"+ex.getMessage());
    }

    @AfterThrowing(value="execution(* *..ISomeService.doThird(..))",throwing="ex")
    public void myAfterThrowing(Exception ex) {
        System.out.println("发生其它异常:"+ex.getMessage());
    }

}

自定义UserException

public class UserException extends Exception {
    public UserException() {
        super();
    }
    public UserException(String message) {
        super(message);
    }
}

自定义UsernameException

public class UsernameException extends UserException {
    public UsernameException() {
        super();
        // TODO Auto-generated constructor stub
    }
    public UsernameException(String message) {
        super(message);
        // TODO Auto-generated constructor stub
    }
}

测试类

异常处理方式一:将异常继续向上抛出,抛给虚拟机

public class MyTest {
    @Test
    public void test() throws UserException {
        String resource = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        ISomeService service = (ISomeService) ac.getBean("someService");

        service.doThird("beijing","1112");

    }

}

异常处理方式二:捕捉异常,自己处理

public class MyTest {
    @Test
    public void test() {
        String resource = "applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
        ISomeService service = (ISomeService) ac.getBean("someService");

        try {
            service.doThird("beijing","1112");
        } catch (UserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

7.5 AspectJ 基于注解的AOP实现 - 最终通知

切面

@Aspect //表示当前类为切面
public class MyAspect {

    //最终通知
    @After("execution(* *..ISomeService.doThird(..))")
    public void myAfter() {
        System.out.println("执行最终通知");
    }

}

不管有没有异常,最终通知都会执行

7.6 AspectJ 基于注解的AOP实现 - 定义切入点

@Aspect //表示当前类为切面
public class MyAspect {

    //最终通知,使用定义的切入点替代表达式
    @After("doThirdPointcut()")
    public void myAfter() {
        System.out.println("执行最终通知");
    }

    //定义切入点
    @Pointcut("execution(* *..ISomeService.doThird(..))")
    public void doThirdPointcut() {}

}

第六章 Spring与AOP AspectJ对AOP的实现

Spring与AspectJ都对AOP这种编程思想进行了实现,但AspectJ实现方式更为简捷方便,而且还支持注解式开发。所以Spring又将AspectJ对于AOP的实现引入到了自己的框架中。

6.1 AspectJ的通知类型

(1)、前置通知

(2)、后置通知

(3)、环绕通知

(4)、异常通知

(5)、最终通知

6.2 AspectJ的切入点表达式

表达式原型:

execution(
[modifiers-pattern] 访问权限类型
ret-type-apttern 返回值类型
[declaring-type-pattern] 全限定性类名
name-pattern(param-pattern) 方法名(参数名)
[throws-pattern] 抛出异常类型
)

其中,返回值类型与方法名(参数名)不能省略。

切入点表达式要匹配的对象就是目标方法的方法名。

  • * :代表0到多个任意字符

  • ..:用在方法参数中,表示任意多个参数;用在包名中表示当前包及其子包路径

  • +:用在类名中,表示当前类及其子类;用在接口中表示当前接口及其实现类

execution(* *..service.*.*(..))

表示指定所有包下的service子包下所有类或接口中所有方法为切入点

6.3 测试环境的搭建

#####(1)、导入jar包

AOP的jar包,AOP联盟的jar包,AspectJ的jar包,spring与AspectJ的整合jar包。

#####(2)、引入AOP的约束