一、连接池

  1. 连接池的概念:将JDBC的操作进行了封装,合理的管理连接资源,在启动的时候会直接创建连接的对象,需要使用操作数据库的时候,直接拿着已经开启的连接使用即可,可以避免频繁的链接数据库操作,减少IO(磁盘IO和网络IO)的操作,以此减少数据库的连接压力

1.1 C3P0连接池

  1. 需要导入C3P0的依赖包:数据库的驱动包、C3P0的核心包和C3P0的依赖包
  2. C3P0的c3p0-config.xml配置文件,要求:文件名称不能修改,并且文件必须放在src目录下
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <c3p0-config>
  3. <default-config>
  4. <!--数据库连接必要的四个参数-->
  5. <property name="driverClass">com.mysql.jdbc.Driver</property>
  6. <property name="jdbcUrl">jdbc:mysql://localhost:3306/db_bank?useUnicode=true&amp;characterEncoding=utf-8</property>
  7. <property name="user">root</property>
  8. <property name="password">root</property>
  9. <!--连接池其他参数的配置-->
  10. <property name="initialPoolSize">5</property>
  11. <property name="maxPoolSize">10</property>
  12. <property name="checkoutTimeout">3000</property>
  13. </default-config>
  14. <mysql>
  15. <!--数据库连接必要的四个参数-->
  16. <property name="driverClass">com.mysql.jdbc.Driver</property>
  17. <property name="jdbcUrl">jdbc:mysql://localhost:3306/db_bank?useUnicode=true&amp;characterEncoding=utf-8</property>
  18. <property name="user">root</property>
  19. <property name="password">root</property>
  20. <!--连接池其他参数的配置-->
  21. <property name="initialPoolSize">5</property>
  22. <property name="maxPoolSize">10</property>
  23. <property name="checkoutTimeout">3000</property>
  24. </mysql>
  25. </c3p0-config>
  1. 用C3P0连接池封装的工具类
  1. public class C3P0Utils {
  2. //声明一个数据源对象
  3. private static DataSource dataSource = new ComboPooledDataSource("mysql");
  4. //获取连接
  5. public static Connection getConn(){
  6. try {
  7. return dataSource.getConnection();
  8. }catch (Exception e){
  9. e.printStackTrace();
  10. }
  11. return null;
  12. }
  13. //关闭资源
  14. public static void close(Object...args){
  15. try {
  16. if(args.length==3 && args[2] != null){
  17. ResultSet rs = (ResultSet) args[2];
  18. rs.close();
  19. }
  20. if(args[1] != null){
  21. PreparedStatement ps =(PreparedStatement)args[1];
  22. ps.close();
  23. }
  24. if(args[0] != null){
  25. Connection conn = (Connection)args[0];
  26. conn.close();
  27. }
  28. }catch (Exception e){
  29. e.printStackTrace();
  30. }
  31. }
  32. }

1.2 Druid连接池

  1. 依赖的jar包:数据库的驱动包、druid的核心包
  2. 创建一个properties文件,名字自定义即可

    1. driverClassName=com.mysql.jdbc.Driver
    2. url=jdbc:mysql://127.0.0.1:3306/db_bank?useUnicode=true&characterEncoding=utf-8
    3. username=root
    4. password=root
  3. 创建Druid工具类

    1. public class DruidUtils {
    2. //声明一个数据源对象
    3. private static DataSource dataSource;
    4. //使用静态代码块实现加载properties文件
    5. static{
    6. try {
    7. //获取properties文件
    8. InputStream in = DruidUtils.class.getClassLoader().getResourceAsStream("db.properties");
    9. Properties prop = new Properties();
    10. prop.load(in);
    11. //将properties对象交给连接池工厂,由工厂创建相应的数据源对象
    12. dataSource = DruidDataSourceFactory.createDataSource(prop);
    13. }catch (Exception e){
    14. e.printStackTrace();
    15. }
    16. }
    17. //创建连接
    18. public static Connection getConn(){
    19. try {
    20. return dataSource.getConnection();
    21. }catch (Exception e){
    22. e.printStackTrace();
    23. }
    24. return null;
    25. }
    26. //关闭资源
    27. public static void close(Object...args){
    28. try {
    29. if(args.length==3 && args[2] != null){
    30. ResultSet rs = (ResultSet) args[2];
    31. rs.close();
    32. }
    33. if(args[1] != null){
    34. PreparedStatement ps =(PreparedStatement)args[1];
    35. ps.close();
    36. }
    37. if(args[0] != null){
    38. Connection conn = (Connection)args[0];
    39. conn.close();
    40. }
    41. }catch (Exception e){
    42. e.printStackTrace();
    43. }
    44. }
    45. }

二、Mybatis概念

  1. Mybatis是一个ORM框架,主要用来Java和数据库操作之间的数据交互,前生ibatis,如果不用Mybatis,操作数据库要么使用原生的JDBC,要么使用连接池。

2.1 原生JDBC中存在的问题

  1. 问题1:sql语句是直接写在了Java代码中,这种写法称为硬编码
    解决:使用mybatis之后,将Java代码和sql语句进行分离,将所有的sql语句抽取出来,配置在xml文件中即可
  2. 问题2:如果需要向数据库sql语句中拼接条件,需要使用?占位符,仍然属于硬编码
    解决:使用mybatis之后,sql语句都在xml文件中,可以使用mybatis提供的输入映射配置,可以自动的将条件和需要的值填充
  3. 问题3:在原生的jdbc中,结果集需要对ResultSet进行遍历,并且依次拿到里面的值,再set到对应的模型中,及其麻烦的硬编码模式
    解决:使用mybaits之后,在执行完sql语句之后,通过输出映射,可以自动的将所有的数据直接填充到模型中

2.2 Mybatis的结构图

Mybatis - 图1

2.3 Mybatis的核心组件

  1. 会话工厂构造器:SqlSessionFactoryBuilder,用来读取核心配置文件并生成工厂对象
  2. 会话工厂:SqlSessionFactory,作用:用来开启会话
  3. 会话:SqlSession,用来获取sql语句,并让执行器执行sql语句,拿到结果集
  4. Mapper代理:sql Mapper代理对象

三、Mybatis入门

3.1 需要依赖的jar包

  1. 需要数据库的驱动包
  2. 需要mybatis的核心包
  3. 需要mybatis的依赖包(工具包和日志包log4j)
    Mybatis - 图2

3.2 开发Mybatis入门代码

  1. 编写核心配置文件:mybatis-config.xml

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <!--配置数据源环境-->
    7. <environments default="development">
    8. <!--配置指定的数据源环境-->
    9. <environment id="development">
    10. <!--给mybatis配置事务管理方式-->
    11. <transactionManager type="JDBC" />
    12. <!--配置数据源需要的配置数据-->
    13. <dataSource type="POOLED">
    14. <property name="driver" value="com.mysql.jdbc.Driver"/>
    15. <property name="url" value="jdbc:mysql://127.0.0.1:3306/db_bank?useUnicode=true&amp;characterEncoding=utf-8"/>
    16. <property name="username" value="root"/>
    17. <property name="password" value="root"/>
    18. </dataSource>
    19. </environment>
    20. </environments>
    21. <!--加载单个mapper文件-->
    22. <mappers>
    23. <mapper resource=""/>
    24. </mappers>
    25. </configuration>
  2. 创建POJO类:目前要求POJO类的属性必须和数据库中字段的属性一致

    1. public class User {
    2. private int user_id;
    3. private String user_name;
    4. private double money;
    5. public int getUser_id() {
    6. return user_id;
    7. }
    8. public void setUser_id(int user_id) {
    9. this.user_id = user_id;
    10. }
    11. public String getUser_name() {
    12. return user_name;
    13. }
    14. public void setUser_name(String user_name) {
    15. this.user_name = user_name;
    16. }
    17. public double getMoney() {
    18. return money;
    19. }
    20. public void setMoney(double money) {
    21. this.money = money;
    22. }
    23. @Override
    24. public String toString() {
    25. return "User{" +
    26. "user_id=" + user_id +
    27. ", user_name='" + user_name + '\'' +
    28. ", money=" + money +
    29. '}';
    30. }
    31. }
  3. 编写单个的Mapper.xml文件,当写好配置文件之后,一定记得到核心配置文件中加载mapper.xml配置文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <!--根目录为mapper标签,需要有属性namespace
    6. namespace:给当前的mapper取一个唯一的名字,用来区别,目前可以自定义
    7. 在Mapper代理方式之后,namespace有特殊的含义
    8. -->
    9. <mapper namespace="userMapper">
    10. <!--需求:根据id值查询用户的数据,结果集只有一条
    11. 属性:
    12. id:给该select标签取一个唯一的标识,目前可以自定义
    13. parameterType:输入映射,填写的是输入参数的数据类型
    14. resultType:输出映射,填写的是输出映射的数据类型,如果是返回的模型对象,输出映射
    15. 填写对应模型的全路径名
    16. #{value}:用来后续将输入映射的实际参数值,填充在这个位置,相当于?
    17. -->
    18. <select id="queryUserById" parameterType="java.lang.Integer" resultType="com.woniuxy.model.User">
    19. <!--编写sql语句-->
    20. select user_id,user_name,money from t_user where user_id = #{value}
    21. </select>
    22. </mapper>
  4. 编写测试类

    1. public class Test {
    2. public static void main(String[] args) throws Exception{
    3. //获取核心配置文件,获取的是输入流
    4. String path = "mybatis-config.xml";
    5. InputStream config = Resources.getResourceAsStream(path);
    6. //获取会话工厂
    7. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    8. //开启会话
    9. SqlSession sqlSession = factory.openSession();
    10. /*执行sql语句
    11. 第一个参数:用来关联执行哪一条sql语句,namespace值.id值
    12. 第二个参数:sql语句需要的实际参数值
    13. */
    14. User user = sqlSession.selectOne("userMapper.queryUserById",1);
    15. System.out.println(user);
    16. }
    17. }

    3.3 Mybatis的其他其他操作

    1. 实现模糊查询
      需求:查询用户名以“小”开头的用户数据
      1)User的POJO类已经创建好了
      2)全局配置文件mybatis-config.xml创建好了,不需要再次配置(如果切换了数据库名需要修改URL,如果有新的xxxMapper。xml文件,需要再次加载,在mappers标签里面再增加一个mapper标签)
      3)再UserMapper.xml配置文件中新增查询标签
      4)测试代码
      重点:#{value}和${value}
      1. <!-- 需求:根据用户名查询出以"小"开头的用户数据 parameterType:输入映射,输入映射String resultType:输出映射,输出多个User对象,仍然是全路径 --> <select id="queryUserrByName" parameterType="java.lang.String" resultType="com.woniuxy.model.User"> select user_id,user_name,money from t_user where user_name like "${value}%" </select>
      1. public class LikeTest {
      2. public static void main(String[] args) throws Exception{
      3. //获取工厂对象
      4. String path = "mybatis-config.xml";
      5. InputStream config = Resources.getResourceAsStream(path);
      6. SqlSessionFactory factory = new
      7. SqlSessionFactoryBuilder().build(config);
      8. //开启会话
      9. SqlSession sqlSession = factory.openSession();
      10. /*执行sql语句并获取结果集
      11. 第一个参数:找到指定的select标签,select标签是sql语句,namespace.id
      12. */
      13. List<User> userList =
      14. sqlSession.selectList("userMapper.queryUserByName","小");
      15. System.out.println(userList);
      16. }
      17. }
      1. 1、#{value}:标识占位符替换,不是字符串,大括号中的值可以任意,一般推荐和POJO中的属性名称一致
      2. 2${value}:用字符串进行拼接,会有sql注入的危险,一般不推荐使用,大括号中的值只能是value

      四、Mybatis的增删改查(MVC开发)

      4.1 搭建环境

  5. 引入依赖的jar包,包含数据库驱动包,mybatis核心包和mybatis依赖包
    Mybatis - 图3

  6. 创建全局配置文件,全局配置文件名称SqlMapConfig.xml,将文件放置在源文件目录下
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <!--配置环境-->
  7. <environments default="development">
  8. <environment id="development">
  9. <!--数据库的事务管理-->
  10. <transactionManager type="JDBC"/>
  11. <!--数据源-->
  12. <dataSource type="POOLED">
  13. <property name="driver" value="com.mysql.jdbc.Driver"/>
  14. <property name="url" value="jdbc:mysql://127.0.0.1:3306/db_book?useUnicode=true&amp;characterEncoding=utf-8"/>
  15. <property name="username" value="root"/>
  16. <property name="password" value="root"/>
  17. </dataSource>
  18. </environment>
  19. </environments>
  20. <mappers>
  21. <!--如果有单个新Mapper.xml文件产生,则引入一个-->
  22. </mappers>
  23. </configuration>
  1. 配置日志文件,日志文件使用log4j,和mybatis的核心配置文件在同一个目录下,并且名字为log4j.properties
  1. # Global logging configuration
  2. log4j.rootLogger=DEBUG, stdout
  3. # MyBatis logging configuration...
  4. log4j.logger.org.mybatis.example.BlogMapper=TRACE
  5. # Console output...
  6. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  7. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  8. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
  1. 创建类(model、POJO、domain)
    Mybatis - 图4

4.2 Mybatis查询操作

  1. 需求:查询所有的书籍信息
  2. 创建一个BookDao接口

    1. public interface BookDao {
    2. //定义一个查询所有书籍数据的方法
    3. public List<Book> queryAllBook() throws Exception;
    4. }
  3. 创建BookMapper.xml文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="bookMapper">
    6. <!--
    7. 需求:查询所有的书籍
    8. parameterType:输入映射,无
    9. resultType:输出映射,Book的实体对象
    10. -->
    11. <select id="queryBook" resultType="com.woniuxy.model.Book">
    12. select book_id,book_name,price,author,number from t_book
    13. </select>
    14. </mapper>
  4. 创建BookDao的实现类,需要查询到所有的数据

    1. public class BookDaoImpl implements BookDao {
    2. /*
    3. 需要操作数据库
    4. */
    5. @Override
    6. public List<Book> queryAllBook() throws Exception {
    7. //获取工厂
    8. String path = "SqlMapConfig.xml";
    9. InputStream config = Resources.getResourceAsStream(path);
    10. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
    11. //开启会话
    12. SqlSession sqlSession = factory.openSession();
    13. //执行
    14. List<Book> bookList = sqlSession.selectList("bookMapper.queryBook");
    15. //关闭会话
    16. sqlSession.close();
    17. return bookList;
    18. }
    19. }
  5. 创建Book的service接口

    1. public interface BookService {
    2. //查询所有的书籍
    3. public List<Book> queryAllBook() throws Exception;
    4. }
  6. 创建Book的service实现类

    1. public class BookServiceImpl implements BookService {
    2. @Override
    3. public List<Book> queryAllBook() throws Exception {
    4. //调用dao中的方法
    5. BookDao bookDao = new BookDaoImpl();
    6. List<Book> bookList = bookDao.queryAllBook();
    7. return bookList;
    8. }
    9. }
  7. 创建Book的controller类

    1. public class BookController {
    2. public static void main(String[] args) {
    3. new BookController().queryAllBook();
    4. }
    5. public void queryAllBook(){
    6. try {
    7. //创建Service对象
    8. BookService bookService = new BookServiceImpl();
    9. List<Book> bookList = bookService.queryAllBook();
    10. System.out.println(bookList);
    11. }catch (Exception e){
    12. e.printStackTrace();
    13. }
    14. }
    15. }

4.3 Mybatis新增操作

  1. 需求:向book表中新增一条数据
  2. 编写dao接口中的方法

    1. //新增数据,将book对象传入
    2. public int addBook(Book book) throws Exception;
  3. 编写Mapper.xml文件

    1. <!--
    2. 需求:新增一条book的数据
    3. parameterType:输入映射,book对象
    4. resultType:输出映射,无
    5. -->
    6. <insert id="insertBook" parameterType="com.woniuxy.model.Book">
    7. insert into t_book(book_name, price, author, number)
    8. value (#{book_name},#{price},#{author},#{number})
    9. </insert>
  4. 编写dao的实现类

    1. @Override
    2. public int addBook(Book book) throws Exception {
    3. //创建工厂
    4. String path = "SqlMapConfig.xml";
    5. InputStream config = Resources.getResourceAsStream(path);
    6. SqlSessionFactory factory = new
    7. SqlSessionFactoryBuilder().build(config);
    8. //开启会话
    9. SqlSession sqlSession = factory.openSession();
    10. //执行方法,返回值标识数据库中修改的行数,新增标识新增的行数
    11. int row = sqlSession.insert("bookMapper.insertBook",book);
    12. System.out.println(row);
    13. //提交事务
    14. sqlSession.commit();
    15. //关闭会话
    16. sqlSession.close();
    17. return row;
    18. }
  5. 编写Service接口

    1. //新增一条book数据
    2. public int addBook(Book book) throws Exception;
  6. 编写service实现类

    1. @Override
    2. public int addBook(Book book) throws Exception {
    3. //调用dao中的方法
    4. BookDao bookDao = new BookDaoImpl();
    5. int row = bookDao.addBook(book);
    6. return row;
    7. }
  7. 编写controller中的方法

    1. //新增book数据
    2. public void addBook(){
    3. try {
    4. Scanner in = new Scanner(System.in);
    5. Book book = new Book();
    6. //获取书籍的数据
    7. System.out.print("请输入书籍名称");
    8. String book_name = in.next();
    9. System.out.print("请输入单价:");
    10. double price = in.nextDouble();
    11. System.out.print("请输入作者:");
    12. String author = in.next();
    13. System.out.print("请输入数量:");
    14. int number = in.nextInt();
    15. //将数据封装到book对象中
    16. book.setAuthor(author);
    17. book.setBook_name(book_name);
    18. book.setNumber(number);
    19. book.setPrice(price);
    20. //创建Service对象
    21. BookService bookService = new BookServiceImpl();
    22. int row = bookService.addBook(book);
    23. if(row > 0){
    24. System.out.println("新增成功");
    25. }else{
    26. System.out.println("新增失败");
    27. }
    28. }catch (Exception e){
    29. e.printStackTrace();
    30. }
    31. }
  8. 在实际开发中,经常需要在新增数据之后,立即获取当前新增数据的主键id值。如果使用了selectKey标签,mybatis会将主键的值映射到传入的对象中,在dao中获取的时候,直接用传入的对象.get主键属性();

    1. <!--
    2. 需求:新增一条book的数据
    3. parameterType:输入映射,book对象
    4. resultType:输出映射,无
    5. -->
    6. <insert id="insertBook" parameterType="com.woniuxy.model.Book">
    7. <!--在新增之后,立即获取刚刚新增数据的主键
    8. keyColumn:指定数据库中作为主键的字段名称
    9. keyProperty:在pojo模型中代表主键的属性名称
    10. resultType:主键对应的数据类型
    11. -->
    12. <selectKey keyColumn="book_id" order="AFTER"
    13. keyProperty="book_id" resultType="java.lang.Integer"> select last_insert_id()
    14. </selectKey>
    15. insert into t_book(book_name, price, author, number)
    16. value (#{book_name},#{price},#{author},#{number})
    17. </insert>

4.4 Mybatis修改操作

  1. 需求:根据id值修改指定书籍的单价
  2. 编写dao的接口

    1. //根据书籍的id值,修改单价
    2. public int updateBook(Book book) throws Exception;
  3. 编写mapper.xml配置文件

    1. <!--
    2. 需求:根据id值,修改书籍的单价
    3. parameterType:输入映射,输入book对象
    4. resultType:输出映射,没有
    5. -->
    6. <update id="editBookById" parameterType="com.woniuxy.model.Book"> update t_book set price = #{price} where book_id = #{book_id} </update>
  4. 编写dao的实现类

    1. @Override
    2. public int updateBook(Book book) throws Exception {
    3. //获取工厂
    4. String path = "SqlMapConfig.xml";
    5. InputStream config = Resources.getResourceAsStream(path);
    6. SqlSessionFactory factory = new
    7. SqlSessionFactoryBuilder().build(config);
    8. //开启会话
    9. SqlSession sqlSession = factory.openSession();
    10. //执行sql语句
    11. int row = sqlSession.update("bookMapper.editBookById",book);
    12. //提交会话
    13. sqlSession.commit();
    14. //关闭会话
    15. sqlSession.close();
    16. return row;
    17. }
  5. 编写service接口

    1. //根据书籍的id值修改书籍的单价
    2. public int updateBookById(Book book) throws Exception;
  6. 编写service的实现类

    1. @Override
    2. public int updateBookById(Book book) throws Exception {
    3. //调用dao中的方法
    4. BookDao bookDao = new BookDaoImpl();
    5. int row = bookDao.updateBook(book);
    6. return row;
    7. }
  7. 编写controller中的方法

    1. //修改书籍的单价
    2. public void updateBook() throws Exception{
    3. Scanner in = new Scanner(System.in);
    4. System.out.print("请输入书籍编号:");
    5. int book_id = in.nextInt();
    6. System.out.print("请输入书籍的单价:");
    7. double price = in.nextDouble();
    8. Book book = new Book();
    9. book.setBook_id(book_id);
    10. book.setPrice(price);
    11. //创建Service对象
    12. BookService bookService = new BookServiceImpl();
    13. int row = bookService.updateBookById(book);
    14. if(row > 0){
    15. System.out.println("修改成功");
    16. }else{
    17. System.out.println("修改失败");
    18. }
    19. }

4,5 Mybatis删除操作

  1. 根据用户的id值删除用户数据
  2. 创建UserDao的接口

    1. public interface UserDao {
    2. //根据id值删除用户数据
    3. public int deleteById(int user_id) throws Exception;
    4. }
  3. 创建UserMapper.xml配置文件,因为是新建的mapper.xml配置文件,需要在核心配置文件中加载该配置文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    3. <mapper namespace="userMapper">
    4. <!--
    5. 需求:根据用户的id值,删除用户的数据
    6. parameterType:输入映射,输入用户id值
    7. resultType:输出映射,无
    8. -->
    9. <delete id="deleteUser" parameterType="int">
    10. delete from t_user where user_id=#{user_id}
    11. </delete>
    12. </mapper>
  4. 创建UserDao的实现类

    1. public class UserDaoImpl implements UserDao {
    2. @Override
    3. public int deleteById(int user_id) throws Exception {
    4. //获取工厂
    5. String path = "SqlMapConfig.xml";
    6. InputStream config = Resources.getResourceAsStream(path);
    7. SqlSessionFactory factory = new
    8. SqlSessionFactoryBuilder().build(config);
    9. //开启会话
    10. SqlSession sqlSession = factory.openSession();
    11. //执行方法
    12. int row = sqlSession.delete("userMapper.deleteUser",1);
    13. //提交事务
    14. sqlSession.commit();
    15. //关闭会话
    16. sqlSession.close();
    17. return row;
    18. }
    19. }
  5. 创建User的业务层接口

    1. //根据id值删除用户
    2. public int deleteUser(int user_id) throws Exception;
  6. 创建User的业务层实现类

    1. public class UserServiceImpl implements UserService {
    2. @Override
    3. public int deleteUser(int user_id) throws Exception {
    4. //调用dao中的方法
    5. UserDao userDao = new UserDaoImpl();
    6. int row = userDao.deleteById(user_id);
    7. return row;
    8. }
    9. }
  7. 编写controller

    1. public void deleteUser() throws Exception{
    2. Scanner in = new Scanner(System.in);
    3. System.out.print("请输入要删除的编号:");
    4. int user_id = in.nextInt();
    5. UserService userService = new UserServiceImpl();
    6. int row = userService.deleteUser(user_id);
    7. if(row > 0){
    8. System.out.println("删除成功");
    9. }else{
    10. System.out.println("删除失败");
    11. }
    12. }

4.6 Junit单元测试

  1. 需要单独执行的方法,可以使用Junit单元测试,让方法可以脱离主方法直接执行,要求该方法不能有形式参数,要求该方法不能是静态的。
  2. Junit一般使用的注解有三个
    @Test:要单独执行的方法上添加的注解
    @Before:在单独执行的方法之前就要执行的注解
    @After:在单独执行的方法之后需要执行的注解
  3. 事例代码
    1. public class JunitTest {
    2. @Test
    3. public void test(){
    4. System.out.println("单元测试的方法");
    5. }
    6. @Before
    7. public void before(){
    8. System.out.println("之前的方法");
    9. }
    10. @After
    11. public void after(){
    12. System.out.println("之后执行");
    13. }
    14. }

五、Mapper代理方式

  1. 新增一个Mapper接口,用Mapper接口替代以前的Dao接口,dao的实现类可以省略,mybatis会自动地完成dao实现类中的方法执行。
  2. 如果要使用Mapper代理方式实现代码执行,需要遵循以下规则:
    1、namespace的值必须式对应的Mapper接口的全路径名
    2、id:必须和mapper接口中定义方法的方法名称一致
    3、parameterType:必须和mapper接口中定义方法的形式参数数据类型一致
    4、resultType:必须和mapper接口中定义的方法的返回值数据类型一致


以根据id值查询用户数据为例:

  1. 编写Mapper接口

    1. public interface UserMapper {
    2. //根据用户id查询用户的值
    3. public User queryUserById(int user_id) throws Exception;
    4. }
  2. 编写Mapper.xml配置文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.woniuxy.mapper.UserMapper">
    6. <!--查询所有的用户
    7. parameterType:输入映射,用户的id值
    8. resultType:输出映射,用户对象
    9. public User queryAllUser(int user_id) throws Exception;
    10. 如果使用mapper代理方式实现,需要遵循以下规则:
    11. id:必须和mapper接口中定义方法的方法名称一致
    12. parameterType:必须和mapper接口中定义方法的形式参数数据类型一致
    13. resultType:必须和mapper接口中定义的方法的返回值数据类型一致
    14. -->
    15. <select id="queryUserById" parameterType="int" resultType="com.woniuxy.model.User">
    16. select user_id,user_name,tel,password from t_user where user_id = #{user_id}
    17. </select>
    18. </mapper>
  3. 编写Service接口

    1. public interface UserService {
    2. //根据id查询用户数据
    3. public User queryUserById(int user_id) throws Exception;}
  4. 编写Service实现类

    1. public class UserServiceImpl implements UserService {
    2. @Override
    3. public User queryUserById(int user_id) throws Exception {
    4. //获取工厂
    5. SqlSessionFactory factory = FactoryUtil.newInstance();
    6. //开启会话
    7. SqlSession sqlSession = factory.openSession();
    8. //获取mapper对象
    9. UserMapper userMapper =
    10. sqlSession.getMapper(UserMapper.class);
    11. //执行接口中的方法
    12. User user = userMapper.queryUserById(user_id);
    13. //关闭会话
    14. sqlSession.close();
    15. return user;
    16. }
    17. }
  5. 编写controller

    1. public class UserController {
    2. @Test
    3. public void queryUserById() throws Exception{
    4. UserService userService = new UserServiceImpl();
    5. User user = userService.queryUserById(2);
    6. System.out.println(user);
    7. }
    8. }

    六、Mybatis的配置

    6.1 Mybatis配置文件中读取properties文件

  6. 在源目录下创建db.properties配置文件,里面用于存放连接数据库的核心数据

    1. driver=com.mysql.jdbc.Driver
    2. url=jdbc:mysql://127.0.0.1:3306/db_book?useUnicode=true&characterEncoding=utf-8
    3. username=root
    4. password=root
  7. 在Mybatis核心配置文件中,加载db.properties,并且将里面的四个参数读取到xml配置文件中

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <!--读取properties配置文件-->
    7. <properties resource="db.properties" />
    8. <!--配置环境-->
    9. <environments default="development">
    10. <environment id="development">
    11. <!--数据库的事务管理-->
    12. <transactionManager type="JDBC"/>
    13. <!--数据源-->
    14. <dataSource type="POOLED">
    15. <property name="driver" value="${driver}"/>
    16. <property name="url" value="${url}"/>
    17. <property name="username" value="${username}"/>
    18. <property name="password" value="${password}"/>
    19. </dataSource>
    20. </environment>
    21. </environments>
    22. <mappers>
    23. <!--如果有单个新Mapper.xml文件产生,则引入一个-->
    24. <mapper resource="mapper/BookMapper.xml"></mapper>
    25. </mappers>
    26. </configuration>

6.2 设置别名

  1. 输入映射和输出映射的时候,每次都需要写全路径,给每一个路径取一个别名,之后再使用该类作为输入映射和输出映射的时候,可以直接使用别名即可。
  2. 设置别名是在mybatis的核心配置文件中设置
    方式1:每次给单个模型类设置别名,比较麻烦
    方式2:使用包扫描的方式实现别名设置,推荐方式
    1. <!--设置别名-->
    2. <typeAliases>
    3. <!--
    4. type:表示要设置别名的全路径
    5. alias:给该路径取得别名
    6. -->
    7. <typeAlias type="com.woniuxy.model.Book" alias="book" />
    8. <typeAlias type="com.woniuxy.model.User" alias="user" />
    9. </typeAliases>
    1. <typeAliases>
    2. <!--使用包扫描的方式:要求所有的模型类需要在同一个包下
    3. 如果使用包扫描,那么别名默认为类名,可以是类名的小驼峰
    4. 比如:类名Book 别名:Book或者book
    5. 类名:UserBook 别名:UserBook 或者userBook
    6. -->
    7. <package name="com.woniuxy.model"/>
    8. </typeAliases>

6.3 使用mapper的包扫描

  1. 如果开发中使用的是Mapper代理方式,那么可以在mybatis的核心配置文件中,在加载mapper的时候,可以使用包扫描的方式
  2. 如果使用包扫描,需要遵循两个规则
    规则1:mapper接口和mapper.xml必须放在同一个包下
    规则2:mapper接口名称和mapper.xml名称必须一致
  1. <mappers>
  2. <package name = "com.woniuxy.mapper"></package>
  3. </mappers>

七、输入映射和输出映射

7.1 输入映射

  1. 输入映射有两种方式:parameterType和parameterMap,如果只有一个参数,写上该参数的数据类型即可,如果需要多个参数,可以选择先将数据封装到对象中,将对象作为输入映射即可。

7.2 输出映射

  1. 输出映射有两种方式:resultType和resultMap
  2. resultType输出映射,可以输出一个对象的属性,会将数据直接封装到对象中,resultType输出映射的值是简单类型,要求查询的数据库结果集必须是一行一列的结果集。
  3. resultMap输出映射,resultMap用在复杂的查询语句里面,它不能作为简单的映射,需要自定义一个resultMap标签,并且对每一个数据做逐一的映射关系
  4. 需求:查询所有的书籍数据,用resultMap方式进行输出映射

    1. <!--
    2. 需求:查询所有的书籍信息,要求使用resultMap输出映射
    3. public List<Book> queryBook() throws Exception;
    4. -->
    5. <!--自定义resultMap标签
    6. id:表示给给resultMap取一个唯一的标识符
    7. type:表示该结果集要输出到哪一个对象对应的模型类全路径,可以使用别名
    8. resultMap标签和select标签输出映射关联是通过id值和resultMap值一致进行关联
    9. -->
    10. <resultMap id="queryBookMap" type="Book">
    11. <!--
    12. id标签:用来映射数据库中主键和属性中代表主键的属性名称映射关系
    13. column:数据库中代表主键的字段名称
    14. property:模型类中用来代表主键的属性名称
    15. -->
    16. <id column="book_id" property="book_id" />
    17. <!--
    18. result标签:用来映射数据库中除了主键以外的其他所有字段
    19. column:数据库中代表除了主键主键以外的字段名称
    20. property:模型类中用来代表除了主键以外的属性名称
    21. -->
    22. <result column="book_name" property="book_name" />
    23. <result column="price" property="price" />
    24. <result column="author" property="author" />
    25. <result column="number" property="number" />
    26. </resultMap>
    27. <select id="queryBook" resultMap="queryBookMap">
    28. select book_id,book_name,price,author,number from t_book
    29. </select>
  5. 总结:在实际开发中,单表查询建议使用resultType输出映射,只要是多表查询,建议使用resultMap,实际开发中,一般情况resultMap居多。

  6. resultType不能支持延迟加载,而resultMap可以支持延迟加载

八、多表查询

8.1 一对一关系映射

  1. 需求:根据用户的id查询用户数据及用户信息数据
  • 输出映射使用resultType方式进行:输出映射必须是同一个模型类
  1. 需要对模型类进行重构

    1. //省略了get和set方法,自己添加
    2. public class UserCustom extends User{
    3. private int infoId;
    4. private int userId;
    5. private String realName;
    6. private String address;
    7. private int height;
    8. private double weight;
    9. }
  2. 编写mapper接口

    1. public interface UserMapper {
    2. //根据id值查询用户数据及用户信息数据
    3. public UserCustom queryUserAndInfoById(int userId) throws Exception;
    4. }
  3. 编写mapper.xml配置文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.woniuxy.mapper.UserMapper">
    6. <!--
    7. 需求:根据id查询用户数据及用户信息数据
    8. public UserCustom queryUserAndInfoById(int userId) throws Exception;
    9. -->
    10. <select id="queryUserAndInfoById" parameterType="int" resultType="UserCustom">
    11. select u.user_id,user_name,tel,password,info_id,real_name,address,height,weight
    12. from
    13. db_book.t_user as u join t_info ti
    14. on
    15. u.user_id = ti.user_id
    16. where
    17. u.user_id = #{user_id}
    18. </select>
    19. </mapper>
  4. 编写service接口

    1. public interface UserService {
    2. //查询用户及用户信心数据
    3. public UserCustom queryUserAndInfoById(int userId) throws Exception;
    4. }
  5. 编写service的实现类

    1. public class UserServiceImpl implements UserService {
    2. @Override
    3. public UserCustom queryUserAndInfoById(int userId) throws Exception {
    4. //获取工厂
    5. SqlSessionFactory factory = FactoryUtil.newInstance();
    6. //开启会话
    7. SqlSession sqlSession = factory.openSession();
    8. //获取mapper对象
    9. UserMapper userMapper =
    10. sqlSession.getMapper(UserMapper.class);
    11. UserCustom userCustom =
    12. userMapper.queryUserAndInfoById(userId);
    13. //关闭会话
    14. sqlSession.close();
    15. return userCustom;
    16. }
    17. }
  6. 编写controller代码

    1. public class UserController {
    2. @Test
    3. public void queryUserAndInfoById()throws Exception{
    4. //获取service对象
    5. UserService userService = new UserServiceImpl();
    6. UserCustom userCustom =
    7. userService.queryUserAndInfoById(2);
    8. System.out.println(userCustom);
    9. }
    10. }
  • 映射输出使用resultMap方式(还是一对一)
    需要找到主表和附表,主表:t_user表,附表:t_info表
  1. 重构pojo的模型类:重构主表的pojo类

    1. //省略了get和set方法
    2. public class User {
    3. private int userId;
    4. private String userName;
    5. private String password;
    6. private String tel;
    7. //在这里声明一个info对象
    8. private Info info;
    9. }
  2. 编写mapper接口

    1. //根据id值查询用户及用户信息的数据
    2. public User queryUserAndInfoById2(int userId) throws Exception;
  3. 编写mapper.xml配置文件,输出映射使用resultMap

    1. <!--
    2. 需求:根据id查询用户数据及用户信息数据
    3. public User queryUserAndInfoById2(int userId) throws Exception;
    4. -->
    5. <!--自定义resultMap
    6. 多表查询的时候,type表示映射的主表对应的pojo类的全路径,可以使用别名 -->
    7. <resultMap id="queryUserAndInfoById2Map" type="User">
    8. <!--多表查询(这里是一对一),首先关联主表和pojo类的映射关系-->
    9. <!--
    10. id标签:表示主表中用主键和主键对应的pojo类中属性的关联映射
    11. column:数据库中主表代表主键的字段
    12. property:属性中代表主键的属性名称
    13. -->
    14. <id column="user_id" property="userId"/>
    15. <!--主表中的其他字段,用result标签进行关联映射
    16. column:数据库中对应的字段名称
    17. property:属性中对应的属性名称
    18. -->
    19. <result column="user_name" property="userName" />
    20. <result column="tel" property="tel" />
    21. <result column="password" property="password" />
    22. <!--关联附表中的属性和字段映射,因为是一对一的关系映射,因此使用标签association
    23. property:主表对应的pojo类中声明的附表pojo类对象简单User类中的info对象 javaType:附表对应的pojo类全路径,可以适应别名,info对象对应的全路径-->
    24. <association property="info" javaType="Info">
    25. <!--重复主表中的操作,分别将主键和普通字段与pojo类的属性进行关联映射-->
    26. <id column="info_id" property="infoId" />
    27. <result column="user_id" property="userId" />
    28. <result column="real_name" property="realName" />
    29. <result column="address" property="address" />
    30. <result column="height" property="height" />
    31. <result column="weight" property="weight" />
    32. </association>
    33. </resultMap>
    34. <select id="queryUserAndInfoById2" parameterType="int" resultMap="queryUserAndInfoById2Map">
    35. select u.user_id,user_name,tel,password,info_id,real_name,address,height,weight
    36. from
    37. db_book.t_user as u join t_info ti
    38. on
    39. u.user_id = ti.user_id
    40. where
    41. u.user_id = #{user_id}
    42. </select>
  4. 编写service接口

    1. //查询用户及用户信息数据
    2. public User queryUserAndInfoById2(int userId) throws Exception;
  5. 编写service实现类

    1. @Override
    2. public User queryUserAndInfoById2(int userId) throws Exception {
    3. //获取工厂
    4. SqlSessionFactory factory = FactoryUtil.newInstance();
    5. //开启会话
    6. SqlSession sqlSession = factory.openSession();
    7. //获取mapper对象
    8. UserMapper userMapper =
    9. sqlSession.getMapper(UserMapper.class);
    10. User user = userMapper.queryUserAndInfoById2(userId);
    11. //关闭会话
    12. sqlSession.close();
    13. return user;
    14. }
  6. 编写conotroller中的代码

    1. @Test
    2. public void queryUserAndInfoById2()throws Exception{
    3. //获取service对象
    4. UserService userService = new UserServiceImpl();
    5. User user = userService.queryUserAndInfoById2(2);
    6. System.out.println(user);
    7. }

8.2 一对多关系映射

  • 需求:根据用户id值,查询用户数据及用户对应的订单数据
  • 主表:用户表,附表:订单表
  1. 对User表进行重构

    1. //这里省略了get和set方法
    2. public class User {
    3. private int userId;
    4. private String userName;
    5. private String password;
    6. private String tel;
    7. //在这里声明一个Order对象的集合
    8. private List<Order> orderList;
    9. }
  2. 编写mapper接口

    1. //根据用户的id值,查询用户数据及用户购买的订单数据
    2. public User queryUserAndOrderById(int userId) throws Exception;
  3. 编写mapper.xml配置文件

    1. <!--
    2. 需求:根据用户的id值,查询用户数据及用户购买的订单数据
    3. public User queryUserAndOrderById(int userId) throws Exception;
    4. -->
    5. <!--自定义resultMap-->
    6. <resultMap id="queryUserAndOrderByIdMap" type="User">
    7. <!--先映射主表关联-->
    8. <id column="user_id" property="userId" />
    9. <result column="user_name" property="userName"/>
    10. <result column="tel" property="tel"/>
    11. <result column="password" property="password"/>
    12. <!--关联附表的映射关系,这里是一对多的关系,collection标签-->
    13. <collection property="orderList" ofType="Order">
    14. <id column="order_id" property="orderId" />
    15. <result column="user_id" property="userId" />
    16. <result column="create_time" property="createTime" />
    17. <result column="note" property="note" />
    18. </collection>
    19. </resultMap>
    20. <select id="queryUserAndOrderById" parameterType="int" resultMap="queryUserAndOrderByIdMap">
    21. select tu.user_id,user_name,password,tel,order_id,create_time,note from
    22. db_book.t_user as tu join t_order t
    23. on
    24. tu.user_id = t.user_id
    25. where
    26. tu.user_id = #{userId}
    27. </select>
  4. 编写service接口

    1. //根据id值查询用户及用户对应的订单数据
    2. public User queryUserAndOrderById(int userId) throws Exception;
  5. 编写service的实现类

    1. @Override
    2. public User queryUserAndOrderById(int userId) throws Exception {
    3. //获取工厂
    4. SqlSessionFactory factory = FactoryUtil.newInstance();
    5. //开启会话
    6. SqlSession sqlSession = factory.openSession();
    7. //获取mapper对象
    8. UserMapper userMapper =
    9. sqlSession.getMapper(UserMapper.class);
    10. User user = userMapper.queryUserAndOrderById(userId);
    11. //关闭会话
    12. sqlSession.close();
    13. return user;
    14. }
  6. 编写controller

    1. @Test
    2. public void queryUserAndOrderById()throws Exception{
    3. //获取service对象
    4. UserService userService = new UserServiceImpl();
    5. User user = userService.queryUserAndOrderById(2);
    6. System.out.println(user);
    7. }

8.3 多对多关系映射

  • 需求:查询出所有用户购买的所有书籍
  • 主表:用户表,附表:订单表,订单明细表,书籍表
  1. 对pojo类进行重构

    1. 1、重构User
    2. public class User {
    3. private int userId;
    4. private String userName;
    5. private String password;
    6. private String tel;
    7. //需要通过User类找到Order类,声明成order对象的集合
    8. private List<Order> orderList;
    9. }
    10. 2、重构Order
    11. public class Order {
    12. private int orderId;
    13. private int userId;
    14. private String createTime;
    15. private String note;
    16. //声明订单明细,声明detail对象的集合
    17. private List<Detail> detailList;
    18. }
    19. 3、重构detail
    20. public class Detail {
    21. private int detailId;
    22. private int orderId;
    23. private int bookId;
    24. //声明一个book对象,
    25. private Book book;
    26. }
  2. 编写mapper接口

    1. //查询所有用户的购买的所有商品
    2. public List<User> queryUserAndBook() throws Exception;
  3. 编写Mapper.xml配置文件

    1. <!--
    2. 需求:查询所有用户的购买的所有商品
    3. public List<User> queryUserAndBook() throws Exception;
    4. -->
    5. <!--自定义resultMap-->
    6. <resultMap id="queryUserAndBookMap" type="User">
    7. <!--关联映射主表user的关系-->
    8. <id column="user_id" property="userId" />
    9. <result column="user_name" property="userName" />
    10. <result column="tel" property="tel" />
    11. <result column="password" property="password" />
    12. <!--关联映射user的模型类中的orderList映射,一对多-->
    13. <collection property="orderList" ofType="Order">
    14. <id column="order_id" property="orderId" />
    15. <result column="user_id" property="userId" />
    16. <result column="create_time" property="createTime" />
    17. <result column="note" property="note"/>
    18. <!--关联映射order类中的detailList映射,一对多-->
    19. <collection property="detailList" ofType="Detail">
    20. <id column="detail_id" property="detailId" />
    21. <result column="order_id" property="orderId" />
    22. <result column="book_id" property="bookId" />
    23. <!--关联映射detail中的book对象,一对一-->
    24. <association property="book" javaType="Book"> <id column="book_id" property="bookId" /> <result column="book_name" property="bookName" />
    25. <result column="price" property="price" /> <result column="number" property="number" /> <result column="author" property="author" /> </association>
    26. </collection>
    27. </collection>
    28. </resultMap>
    29. <select id="queryUserAndBook" resultMap="queryUserAndBookMap"> select
    30. tu.user_id,user_name,tel,password, t.order_id,create_time, note,detail_id,tb.book_id,book_name,price,author,number from
    31. db_book.t_user tu
    32. join
    33. t_order t on tu.user_id = t.user_id
    34. join
    35. t_detail td on t.order_id = td.order_id
    36. join
    37. t_book tb on td.book_id = tb.book_id ;
    38. </select>
  4. 编写service接口

    1. //查询所有用户的购买的所有商品
    2. public List<User> queryUserAndBook() throws Exception;
  5. 编写service实现类

    1. @Override
    2. public List<User> queryUserAndBook() throws Exception {
    3. //获取工厂
    4. SqlSessionFactory factory = FactoryUtil.newInstance();
    5. //开启会话
    6. SqlSession sqlSession = factory.openSession();
    7. //获取mapper对象
    8. UserMapper userMapper =
    9. sqlSession.getMapper(UserMapper.class);
    10. List<User> userList = userMapper.queryUserAndBook();
    11. //关闭会话
    12. sqlSession.close();
    13. return userList;
    14. }
  6. 编写controller

    1. @Test
    2. public void queryUserAndBook()throws Exception{
    3. //获取service对象
    4. UserService userService = new UserServiceImpl();
    5. List<User> userList = userService.queryUserAndBook();
    6. System.out.println(userList);
    7. }

    九、动态SQL

    9.1 多条件查询

    需求:根据传入的数据个数,进行分条件的查询

  7. 创建Mapper接口

    1. //根据用户的部分条件进行筛选,将user对象作为形式参数
    2. public List<User> queryUser(User user) throws Exception;
  8. 创建Mapper.xml配置文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.woniuxy.mapper.UserMapper">
    6. <!--
    7. 需求:根据用户的部分条件进行筛选,将user对象作为形式参数
    8. public List<User> queryUser(User user) throws Exception;
    9. -->
    10. <select id="queryUser" parameterType="User" resultType="User">
    11. select
    12. ti.user_id, user_name, tel, password, age, info_id, real_name,
    13. address, height, weight
    14. from
    15. t_user join t_info ti
    16. on
    17. t_user.user_id = ti.user_id
    18. <where>
    19. <if test="age != null and age != ''">
    20. and age &lt; #{age}
    21. </if>
    22. <if test="info.address != null">
    23. and address = #{info.address}
    24. </if>
    25. </where>
    26. </select>
    27. </mapper>
  9. 编写service的接口

    1. //根据不同的条件查询用户数据
    2. public List<User> queryUser(User user) throws Exception;
  10. 编写service实现类

    1. public class UserServiceImpl implements UserService {
    2. @Override
    3. public List<User> queryUser(User user) throws Exception {
    4. //获取工厂
    5. SqlSessionFactory factory = FactoryUtil.newInstance();
    6. //开启会话
    7. SqlSession sqlSession = factory.openSession();
    8. //获取Mapper对象
    9. UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    10. List<User> userList = userMapper.queryUser(user);
    11. //关闭会话
    12. sqlSession.close();
    13. return userList;
    14. }
    15. }
  11. 编写controller

    1. public class UserController {
    2. @Test
    3. public void queryUser() throws Exception{
    4. User user = new User();
    5. //比如:根据地址和年龄进行匹配
    6. user.setAge(18);
    7. Info info = new Info();
    8. info.setAddress("重庆");
    9. user.setInfo(info);
    10. UserService userService = new UserServiceImpl();
    11. List<User> userList = userService.queryUser(user);
    12. System.out.println(userList);
    13. }
    14. }

十、延迟加载

10.1 延迟加载的概念


也叫做懒加载或者按需加载,可以将多表查询用子查询的方式进行拆分,拆分成几个独立的单表查询,需要哪些 数据,只查询需要的即可,如果需要进一步的数据展示,再进行数据库的查询。

10.2 举例说明

  1. mybaits框架默认加载为积极加载,如果要用到延迟加载,需要再核心配置文件中开启延迟加载

    1. <!--开启延迟加载-->
    2. <setting name="lazyLoadingEnabled" value="true"/>
    3. <setting name="aggressiveLazyLoading" value="false"/>
  2. 根据需求,开发一个,满足延迟加载的代码,编写Mapper接口

    1. //查询出所有用户的数据及对应用户的订单数据
    2. public List<User> queryUserAndOrder() throws Exception;
  3. 检查pojo类,区分出主表:user表,附表:order表

    1. public class User {
    2. private int userId;
    3. private String userName;
    4. private String password;
    5. private String tel;
    6. private int age;
    7. //声明info对象
    8. private Info info;
    9. //需要通过User类找到Order类,声明成order对象的集合
    10. private List<Order> orderList;
    11. }
  4. mapper.xml配置文件

    1. <!--
    2. 需求:查询出所有用户的数据及对应用户的订单数据
    3. public List<User> queryUserAndOrder() throws Exception; 要求:使用延迟加载
    4. -->
    5. <!--定义新的查询语句-->
    6. <select id="queryOrderByUserId" parameterType="int" resultType="Order">
    7. select order_id, user_id, create_time, note from t_order where user_id = #{user_id}
    8. </select>
    9. <!--自定义resultMap-->
    10. <resultMap id="queryUserAndOrderMap" type="User">
    11. <!--主表映射-->
    12. <id column="user_id" property="userId" />
    13. <result column="user_name" property="userName" />
    14. <result column="tel" property="tel" />
    15. <result column="password" property="password" />
    16. <result column="age" property="age" />
    17. <!--附表映射,一对多
    18. 延迟加载新增的两个属性:
    19. column:要用于子查询的条件字段
    20. select:新的查询语句,值为新的select的id值
    21. -->
    22. <collection property="orderList" ofType="Order" column="user_id" select="queryOrderByUserId">
    23. <!--如果是延迟加载,这里空着-->
    24. </collection>
    25. </resultMap>
    26. <select id="queryUserAndOrder" resultMap="queryUserAndOrderMap">
    27. select user_id, user_name, tel, password, age from t_user
    28. </select>
  5. service接口

    1. //查询所有用户及用户的订单数据
    2. public List<User> queryUserAndOrder() throws Exception;
  6. service的实现类

    1. @Override
    2. public List<User> queryUserAndOrder() throws Exception {
    3. //获取工厂
    4. SqlSessionFactory factory = FactoryUtil.newInstance();
    5. //开启会话
    6. SqlSession sqlSession = factory.openSession();
    7. //获取Mapper对象
    8. UserMapper userMapper =
    9. sqlSession.getMapper(UserMapper.class);
    10. List<User> userList = userMapper.queryUserAndOrder();
    11. sqlSession.close();
    12. return userList;
    13. }
  7. controller类

    1. @Test
    2. public void queryUserAndOrder()throws Exception{
    3. UserService userService = new UserServiceImpl();
    4. List<User> userList = userService.queryUserAndOrder();
    5. //遍历userList
    6. for(int i = 0;i < userList.size();i++){
    7. User user = userList.get(0);
    8. System.out.println("编号:"+user.getUserId()+",姓名"+user.getUserName());
    9. //获取订单对象
    10. List<Order> orderList = user.getOrderList();
    11. System.out.println(orderList);
    12. }
    13. }

十一、Mybatis的缓存


在mybatis的框架中,存在一级缓存和二级缓存,mybatis的缓存指的是第一次查询连接数据库实现查询功能,第二次如果是相同的查询语句,则取缓存里面拿数据,不再连接数据库,以此降低数据库的网络IO

11.1一级缓存


在Mybatis框架中,一级缓存默认开始,不再需要程序员做处理,一级缓存的作用范围在一个会话期间 (SqlSession)
现在用一个代码演示缓存,查看日志中是否执行了sql语句来判断是否查询了数据库。
实例代码:
效果1:当两条sql语句在同一个sqlSession之间,并且查询的条件一致,查看结果:日志中查询了数据库多少次
Mybatis - 图5
效果2:在一个会话期间,两条查询语句中间新增一条修改的sql语句并提交事务
Mybatis - 图6
效果3:(预留)当修改的语句和缓存区里面的数据无关的时候,是否会清空该缓存

  1. @Override
  2. public void testCache(User user) throws Exception {
  3. //获取工厂
  4. SqlSessionFactory factory = FactoryUtil.newInstance();
  5. //开启会话
  6. SqlSession sqlSession = factory.openSession();
  7. //获取Mapper对象
  8. UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  9. User u1 = userMapper.queryUserById(user.getUserId());
  10. System.out.println("第一次查询:"+u1);
  11. User u2 = userMapper.queryUserById(user.getUserId());
  12. System.out.println("第二次查询:"+u2);
  13. //关闭会话
  14. sqlSession.close();
  15. }

11.2二级缓存


二级缓存相对于一级缓存范围更大,范围区间在整个mapper区间,二级缓存数据存储位置在硬盘,如果要使用二级缓存,需要被操作的pojo类序列化,mybatis框架不会默认开启二级缓存,需要在核心配置文件中开启二级缓存,并且在对应的mapper配置文件中标识该mapper开启二级缓存。

  1. 在核心配置文件中开启二级缓存

    1. <settings>
    2. <!--开启二级缓存-->
    3. <setting name="cacheEnabled" value="true"/> </settings>
  2. 在mapper中开启二级缓存

    1. <mapper namespace="com.woniuxy.mapper.UserMapper">
    2. <!--标识该mapper开启了二级缓存-->
    3. <cache />
    4. </mapper>
  3. 对对应的pojo类进行序列化

    1. public class User implements Serializable { ......}

    测试
    结果1:如果两个一样的查询语句,当开启了二级缓存之后,那么就算不在同一个会话期间,第二次查询还是走的缓存空间
    Mybatis - 图7
    结果2:在两个查询语句之间,做一次更新操作,并提交事务。现象:如果中间有修改操作,则清空缓存,第二次查询走数据库

    十二、使用注解开发MyBatis应用

    1. MyBatis支持注解开发,可使用注解替代映射器XML文件,但对于同一个映射器接口不能既用注解又用映射器XML文件,否则会导致MyBatis无法确定到底使用哪种方式而报错。

    12.1、CURD操作

    MyBatis实现CURD操作有对应的注解:@Select,@Update,@Insert,@Delete。
    需求:使用注解对t_student进行CURD操作。
    创建映射器接口:src/com/wsjy/mapper/StudentMapper2.java ```java public interface StudentMapper2 { /**

    • 查询所有学生信息
    • @return / @Select(“select from t_student”) List findAll();

      /**

    • 根据学号查询学生信息
    • @return / @Select(“select from t_student where sid=#{sid}”) Student findStudentBySid(int sid);

      /**

    • 新增学生
    • @param student */ @Insert(“insert into t_student values(#{sid},#{sname},#{ssex},#{sage},#{saddress},#{sbirthday},#{cid})”) void addStudent(Student student);

      /**

    • 根据学号删除学生信息
    • @param sid */ @Delete(“delete from t_student where sid=#{sid}”) void deleteStudentBySid(int sid);

      /**

    • 根据学号修改学生信息
    • @param student */ @Update(“update t_student set sname=#{sname},ssex=#{ssex} where sid=#{sid}”) void updateStudent(Student student);

}

  1. 在测试类中运行以上方法都可正常执行,从运行结果来看,在查询学生信息时,对应的班级和科目信息为null,代表并没有级联查询班级和科目,可使用注解实现级联查询。
  2. <a name="VLPBA"></a>
  3. #### 12.2、级联
  4. MyBatis通过@Results@Result@One@Many来实现级联操作。
  5. - @Results:<resultMap>
  6. - @Result:<result>,通过id属性来区分<id>(true)和<result>(false或省略不写)
  7. - @One:<association>
  8. - @Many:<collection>
  9. 需求:在查询学生信息的同时,级联查询班级和科目信息<br />修改映射器接口:src/com/wsjy/mapper/StudentMapper2.java
  10. ```java
  11. public interface StudentMapper2 {
  12. ......
  13. /**
  14. * 根据学生编号查询学生信息,级联查询该学生选修的所有科目信息、所属班级信息
  15. * @param sid
  16. * @return
  17. */
  18. @Select({"select * from t_student where sid=#{sid}"})
  19. @Results(id = "studentMap",value = {
  20. @Result(id = true,property = "sid",column = "sid"),
  21. @Result(property = "sname",column = "sname"),
  22. @Result(property = "ssex",column = "ssex"),
  23. @Result(property = "sage",column = "sage"),
  24. @Result(property = "saddress",column = "saddress"),
  25. @Result(property = "sbirthday",column = "sbirthday"),
  26. @Result(property = "cid",column = "cid"),
  27. @Result(property = "classes",column = "cid",
  28. one = @One(select = "com.wsjy.mapper.ClassesMapper.findClassByCid",
  29. fetchType = FetchType.EAGER)),
  30. @Result(property = "subs",column = "sid",
  31. many = @Many(select = "com.wsjy.mapper.SubjectMapper.findSubjectsBySid",
  32. fetchType = FetchType.LAZY))
  33. })
  34. Student findStudentBySid(int sid);
  35. }

鉴别器也可以使用注解:@TypeDiscriminator,@Case来完成。

  1. 从以上案例可以看出,使用注解实质上并没有减少任何配置,只是将映射器XML文件中的配置全部转移到java源文件中来而已,同时造成代码结构混乱,可读性差,SQL语句与java代码严重耦合,程序功能一旦有所变化,必须修改java源码等一系列问题。

12.3、缓存

缓存主要是指二级缓存,因为一级缓存自动开启,可以直接使用。使用注解开启二级缓存,非常简单,只需要在映射器接口上使用@CacheNamespace(blocking = true)即可开启。

  1. @CacheNamespace(blocking = true)
  2. public interface StudentMapper2 {
  3. ......
  4. }

12.4、动态SQL

mybatis3中增加了使用注解来配置Mapper的新特性,常用的注解有:
@SelectProvider
@UpdateProvider
@InsertProvider
@DeleteProvider。

  1. 以上注解用于Mapper接口的方法上,使用type属性来指定由哪个类来提供SQL语句,使用method属性指定由type属性指定的类中哪个具体的方法提供SQL

示例:

  1. public interface UserMapper {
  2. //指定被@SelectProvider注解的方法使用SqlProvider类的selectUser方法提供的SQL语句
  3. @SelectProvider(type = SqlProvider.class, method = "selectUser")
  4. @ResultMap("userMap")
  5. public User getUser(long userId);
  6. }
  1. @SelectProvider注解用于生成查询用的sql语句,有别于@Select注解,@SelectProvide指定一个Class及其方法,并且通过调用Class上的这个方法来获得sql语句。在上例中,获取查询sql的方法是SqlProvider.selectUser
  2. @ResultMap注解用于从查询结果集RecordSet中取数据然后拼装实体bean

编写SqlProvider方式一

  1. public class SqlProvider {
  2. public String selectUser(long userId) {
  3. return "select * from user where userId=" + userId;
  4. }
  5. }

编写SqlProvider方式二

  1. public class SqlProvider{
  2. /**
  3. * list类型:mapper方法形参和sql构建器方法形参必须同时加@Param注解
  4. * @param ids
  5. * @return
  6. */
  7. public String deleteByIdsSql(@Param("ids") List<Integer> ids){
  8. return new SQL(){{
  9. DELETE_FROM("t_test");
  10. String idStr="";
  11. for (int i = 0; i < ids.size(); i++) {
  12. if (i<ids.size()-1)
  13. idStr+=ids.get(i)+",";
  14. else
  15. idStr+=ids.get(i);
  16. }
  17. WHERE("id in ("+idStr+")");
  18. }}.toString();
  19. }
  20. /**
  21. * 单参数:sql构建器方法参数上同时加上@Param注解,
  22. * 单参数时,mapper方法参数上可不加@Param注解,但建议加上
  23. * @return
  24. */
  25. public String selectByIdsSql(@Param("id") Integer id){
  26. return new SQL()
  27. .SELECT("*")
  28. .FROM("t_goods")
  29. .WHERE("id="+id)
  30. .toString();
  31. }
  32. }

使用第二种方式的优势是将SQL语句进行结构化,层次分明,避免了第一种形式书写复杂SQL时不便于阅读的问题,具体规则可参见mybatis官网: https://mybatis.org/mybatis-3/zh/statement-builders.html

注解实现动态SQL拼接

1、编写Mapper层代码

  1. //type:产生SQL语句的类型 method:类中真正产生SQL语句的方法名
  2. @DeleteProvider(type = BuildSQL.class,method = "getSQL")
  3. int deleteByCartId(List<Integer> ids);

2、编写Mapper层注解中的方法

  1. public class BuildSQL{
  2. /**
  3. * 产生SQL语句的方法
  4. * 根据传递的参数,动态构建SQL语句
  5. * @return
  6. */
  7. public String getSQL(List<Integer> ids){
  8. SQL sql = new SQL().DELETE_FROM("t_cart");
  9. String condition="id in(";
  10. //遍历id集合,生成sql条件
  11. for (int i = 0; i < ids.size(); i++) {
  12. condition+=ids.get(i)+",";
  13. }
  14. condition = condition.substring(0, condition.length() - 1)+")";
  15. sql.WHERE(condition);
  16. return sql.toString();
  17. }
  18. }

其他增、改、查操作类似,请查阅mybatis官网中的java api条目下的注解部分。

注意:
如果只想实现一次删除多条数据库表记录,可以不使用mybatis的方式处理,而在业务层中进行处理:将要删除的id集合遍历,在循环中逐条删除。