0%

在WEB中使用mybatis

  • 目标:
    • 掌握 mybatis 在 web 应用中怎么用。
    • mybatis 三大对象的作用域和生命周期。
    • ThreadLocal 原理及使用。
    • 巩固 MVC 架构模式。
    • 为学习 MyBatis 的接口代理机制做准备。
  • 实现功能:
    • 银行账户转账
  • 使用技术:
    • HTML + Servlet + Mybatis
  • Web应用名称:
    • bank

六、在WEB中使用Mybatis

  • 目标:
    • 掌握 mybatis 在 web 应用中怎么用。
    • mybatis 三大对象的作用域和生命周期。
    • ThreadLocal 原理及使用。
    • 巩固 MVC 架构模式。
    • 为学习 MyBatis 的接口代理机制做准备。
  • 实现功能:
    • 银行账户转账
  • 使用技术:
    • HTML + Servlet + Mybatis
  • Web应用名称:
    • bank

6.1 银行账户转账实现

  • 实现步骤,可以从前端往后端写,根据请求一步步往后写。也可以从后端往前端写。

    这里是从前端往后端写。

6.1.1 环境搭建

  • 在 IDEA 中创建 Maven WEB 应用(mybatis-004-web):

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-SSM-notebook/img/Mybatis/maven web模板.png)

  • IDEA 配置 Tomcat,这里 Tomcat 使用 10+ 版本。并部署应用到 tomcat。

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-SSM-notebook/img/Mybatis/tomcat配置.png)

    ![](../../../../../Running Noob/计算机/Typora笔记/笔记-git仓库/Java-SSM-notebook/img/Mybatis/tomcat配置2.png)

  • 这个 web 模板的 web.xml 文件的版本较低,可以从 tomcat10 的样例文件中复制,然后修改。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
    https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
    version="6.0"
    metadata-complete="false">

    </web-app>

    注意:metadata-complete="false" 是指支持 @WebServle("") 注解式开发,如果为 true 则表示不支持注解式开发

  • 删除 index.jsp 文件,因为我们这个项目不使用 JSP,只使用 html。

  • pom.xml 文件中引入相关依赖,包括 mybatismysql驱动,logbackservletjsp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    <?xml version="1.0" encoding="UTF-8"?>

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.f</groupId>
    <artifactId>mybatis-004-web</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>mybatis-004-web Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
    <!--mybatis依赖-->
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.10</version>
    </dependency>
    <!--mysql依赖-->
    <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.2.0</version>
    </dependency>
    <!--logback依赖-->
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.12</version>
    </dependency>
    <!--servlet依赖-->
    <dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>5.0.0</version>
    <scope>provided</scope>
    </dependency>
    <!--jsp依赖-->
    <dependency>
    <groupId>jakarta.servlet.jsp</groupId>
    <artifactId>jakarta.servlet.jsp-api</artifactId>
    <version>3.0.0</version>
    <scope>provided</scope>
    </dependency>
    </dependencies>

    <build>
    <finalName>mybatis-004-web</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
    <plugins>
    <plugin>
    <artifactId>maven-clean-plugin</artifactId>
    <version>3.1.0</version>
    </plugin>
    <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
    <plugin>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.0.2</version>
    </plugin>
    <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.0</version>
    </plugin>
    <plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.1</version>
    </plugin>
    <plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>3.2.2</version>
    </plugin>
    <plugin>
    <artifactId>maven-install-plugin</artifactId>
    <version>2.5.2</version>
    </plugin>
    <plugin>
    <artifactId>maven-deploy-plugin</artifactId>
    <version>2.8.2</version>
    </plugin>
    </plugins>
    </pluginManagement>
    </build>
    </project>
  • 引入相关配置文件,放到 resources 目录下(全部放到类的根路径下)

    • mybatis-config.xml
    • AccountMapper.xml
    • logback.xml
    • jdbc.properties

6.1.2 前端页面index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>银行账户转账</title>
</head>
<body>
<!--/bank是应用的根,部署web应用到tomcat的时候一定要注意这个名字-->
<form action="/bank/transfer" method="post">
转出账户:<input type="text" name="fromActno"/><br>
转入账户:<input type="text" name="toActno"/><br>
转账金额:<input type="text" name="money"/><br>
<input type="submit" value="转账"/>
</form>
</body>
</html>

6.1.3 创建web包、service包、pojo包、dao包、utils包、exceptions包

  • web 包 -> 表示层
  • service 包 -> 业务逻辑层
  • dao 包 -> 持久层(数据访问层)

6.1.4 定义pojo类:Account

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.f.bank.pojo;

/**
* 账户类,封装账户数据
* @author fzy
* @date 2024/1/5 18:28
*/
public class Account {
private Long id;
private String actno;
private Double balance;

public Account() {
}

public Account(Long id, String actno, Double balance) {
this.id = id;
this.actno = actno;
this.balance = balance;
}

@Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
}

public Long getId() {
return id;
}

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

public String getActno() {
return actno;
}

public void setActno(String actno) {
this.actno = actno;
}

public Double getBalance() {
return balance;
}

public void setBalance(Double balance) {
this.balance = balance;
}
}

6.1.5 编写AccountService接口以及AccountServiceImpl

★面向接口编程
  • 在 javaweb 中说过,可以将业务代码分为表示层、业务逻辑层和数据访问层,层与层之间通过接口进行调用,是面向接口编程。

    所以在 service 包中编写接口 AccountService,以及在后面的 dao 包中编写接口 AccountDao。再编写它们的实现类,通过调用实现类具体的方法来实现功能。

    • 注意:将接口写在 service 包和 dao 包下,然后在这两个包下分别再建 impl 包,用于存放创建的实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.f.bank.service;

import com.f.bank.exceptions.MoneyNotEnoughException;
import com.f.bank.exceptions.TransferException;

/**
* 账户业务类接口
*
* @author fzy
* @date 2024/1/6 11:33
*/
public interface AccountService {
/**
* 账户转账业务
* @param fromActno 转出账号
* @param toActno 转入账号
* @param money 转账金额
*/
void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.f.bank.service.impl;

import com.f.bank.dao.AccountDao;
import com.f.bank.dao.impl.AccountDaoImpl;
import com.f.bank.exceptions.MoneyNotEnoughException;
import com.f.bank.exceptions.TransferException;
import com.f.bank.pojo.Account;
import com.f.bank.service.AccountService;

/**
* @author fzy
* @date 2024/1/6 11:36
*/
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao = new AccountDaoImpl();

@Override
public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
// 1.判断转出账户的余额是否充足(select)
Account fromAct = accountDao.selectByActno(fromActno);
if (fromAct.getBalance() < money) {
// 2.如果转出账户的余额不足,提示用户
throw new MoneyNotEnoughException("您的账户余额不足!");
}
// 3.如果转出账户余额充足,更新转出账户余额(update)
// 先更新内存中java对象的信息,再更新数据库
Account toAct = accountDao.selectByActno(toActno);
fromAct.setBalance(fromAct.getBalance() - money);
toAct.setBalance(toAct.getBalance() + money);
int count = accountDao.updateAct(fromAct);
// 4.更新转入账户余额(update)
count += accountDao.updateAct(toAct);
if (count != 2) {
// count不等于2说明转账有异常
throw new TransferException("转账失败, 原因未知");
}
}
}

6.1.6 编写AccountDao接口,以及AccountDaoImpl实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.f.bank.dao;

import com.f.bank.pojo.Account;

/**
* 账户的DAO对象,负责t_act表中数据的CRUD
* 注意:DAO对象中的任何一个方法都和业务不挂钩,没有任何的业务逻辑在里面
* DAO中的方法只做CRUD,所以方法名大部分是:insertXxx、deleteXxx、updateXxx、selectXxx
*
* @author fzy
* @date 2024/1/6 11:43
*/
public interface AccountDao {

/**
* 根据账号查询账户信息
*
* @param actno
* @return
*/
Account selectByActno(String actno);

/**
* 更新账户信息
*
* @param act 账户对象
* @return 1表示更新成功,其他值表示更新失败
*/
int updateAct(Account act);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.f.bank.dao.impl;

import com.f.bank.dao.AccountDao;
import com.f.bank.pojo.Account;
import com.f.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;

/**
* @author fzy
* @date 2024/1/6 11:51
*/
public class AccountDaoImpl implements AccountDao {
@Override
public Account selectByActno(String actno) {
SqlSession sqlSession = SqlSessionUtil.openSession();
Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);
sqlSession.close();
return account;
}

@Override
public int updateAct(Account act) {
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.update("account.updateAct", act);
sqlSession.commit();
sqlSession.close();
return count;
}
}

6.1.7 编写AccountServlet

  • AccountServlet 即为 controller,由它来调度 modelview,以完成用户的请求。

    逻辑为:

    1. 获取用户提交的表单数据。
    2. 调用 AccountService 的转账方法完成转账(调业务层)。
      • AccountService 中需要有 AccountDao,才能完成对数据库的操作。
      • AccountDao 的实现类中,使用 mybatis。
    3. 调用 view 完成展示结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.f.bank.web;

import com.f.bank.exceptions.MoneyNotEnoughException;
import com.f.bank.exceptions.TransferException;
import com.f.bank.service.AccountService;
import com.f.bank.service.impl.AccountServiceImpl;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
* @author fzy
* @date 2024/1/6 11:18
*/

@WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
// 为了让这个对象在其他方法中也可以用,将其声明为属性
private AccountService accountService = new AccountServiceImpl();

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取表单数据
String fromActno = request.getParameter("fromActno");
String toActno = request.getParameter("toActno");
double money = Double.parseDouble(request.getParameter("money"));
try {
// 调用service的转账方法完成转账(调业务层)
accountService.transfer(fromActno, toActno, money);
// 调用view完成展示结果
response.sendRedirect(request.getContextPath() + "/success.jsp");
} catch (MoneyNotEnoughException e) {
request.setAttribute("message", e.getMessage());
request.getRequestDispatcher("/error.jsp").forward(request, response);
} catch (TransferException e) {
request.setAttribute("message", e.getMessage());
request.getRequestDispatcher("/error.jsp").forward(request, response);
}
}
}

6.1.8 其他…

  • 还有转账成功后的页面、转账失败后的页面、自定义的异常、SqlSessionUtil 等代码,就不贴在这里了,可以去看 mybatis-004-web 的项目源代码。

6.2 Mybatis事务问题

  • 在 6.1 小节中,还有一个问题,就是在 AccountServiceImpl 代码中,还有一个事务的问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Override
    public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
    // 1.判断转出账户的余额是否充足(select)
    Account fromAct = accountDao.selectByActno(fromActno);
    if (fromAct.getBalance() < money) {
    // 2.如果转出账户的余额不足,提示用户
    throw new MoneyNotEnoughException("您的账户余额不足!");
    }
    // 3.如果转出账户余额充足,更新转出账户余额(update)
    // 先更新内存中java对象的信息,再更新数据库
    Account toAct = accountDao.selectByActno(toActno);
    fromAct.setBalance(fromAct.getBalance() - money);
    toAct.setBalance(toAct.getBalance() + money);
    int count = accountDao.updateAct(fromAct);
    // 4.更新转入账户余额(update)
    count += accountDao.updateAct(toAct);
    if (count != 2) {
    // count不等于2说明转账有异常
    throw new TransferException("转账失败, 原因未知");
    }
    }

    在更新账户对象,即下面的代码时:

    1
    2
    3
    int count = accountDao.updateAct(fromAct);
    // 如果有其他代码,且这些代码中出现异常
    count += accountDao.updateAct(toAct);

    如果上一行代码执行成功,下一行代码执行失败,虽然会抛出异常,但在数据库中,上一行代码是实际地改变了数据库的数据,所以就会造成数据错误。因此,我们需要在 AccountServiceImpl 中进行事务的提交,而非在 AccountDaoImpl 中进行事务的提交

★ThreadLocal

  • 另外还有一个问题,在 6.1 节中,AccountDaoImpl 中两个方法的 SqlSession 对象并非是同一个,这也会影响事务的控制。为了让 AccountServiceImplAccountDaoImpl 的两个方法中的 SqlSession 对象是同一个,我们需要使用 ThreadLocal

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package com.f.bank.dao.impl;

    import com.f.bank.dao.AccountDao;
    import com.f.bank.pojo.Account;
    import com.f.bank.utils.SqlSessionUtil;
    import org.apache.ibatis.session.SqlSession;

    /**
    * @author fzy
    * @date 2024/1/6 11:51
    */
    public class AccountDaoImpl implements AccountDao {
    @Override
    public Account selectByActno(String actno) {
    SqlSession sqlSession = SqlSessionUtil.openSession(); //一个SqlSession对象
    Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);
    sqlSession.close();
    return account;
    }

    @Override
    public int updateAct(Account act) {
    SqlSession sqlSession = SqlSessionUtil.openSession(); //另一个SqlSession对象
    int count = sqlSession.update("account.updateAct", act);
    sqlSession.commit();
    sqlSession.close();
    return count;
    }
    }
  • 修改后的代码如下:

    • SqlSessionUtil 中的代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      package com.f.bank.utils;

      import org.apache.ibatis.io.Resources;
      import org.apache.ibatis.session.SqlSession;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.apache.ibatis.session.SqlSessionFactoryBuilder;

      /**
      * @author fzy
      * @date 2023/12/31 20:59
      */
      public class SqlSessionUtil {
      private static SqlSessionFactory sqlSessionFactory = null;
      // 增加ThreadLocal对象
      private static ThreadLocal<SqlSession> local = new ThreadLocal<>();

      static {
      try {
      // SqlSessionFactory对象:一个SqlSessionFactory对应一个environment,一个environment通常是一个数据库。
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
      } catch (Exception e) {
      e.printStackTrace();
      }
      }

      // 工具类的构造方法一般都是私有化的。
      // 工具类中所有的方法都是静态的,直接采用类名即可调用。不需要new对象。
      // 为了防止new对象,构造方法私有化。
      private SqlSessionUtil() {
      }

      /**
      * 获取 SqlSession 对象
      */
      //public static SqlSession openSession() {
      // return sqlSessionFactory.openSession();
      //}
      // 将上面的代码改为下面的代码
      public static SqlSession openSession() {
      SqlSession sqlSession = local.get();
      if (sqlSession == null) {
      sqlSession = sqlSessionFactory.openSession();
      // 将SqlSession对象绑定到当前线程上
      local.set(sqlSession);
      }
      return sqlSession;
      }

      /**
      * 关闭SqlSession对象(从当前线程中移除SqlSession对象)
      *
      * @param sqlSession
      */
      public static void close(SqlSession sqlSession) {
      if (sqlSession != null) {
      sqlSession.close();
      // 解绑SqlSession对象。
      // 因为Tomcat服务器支持线程池,也就是说,用过的线程对象t1,可能下一次还会使用这个t1线程,
      // 为了避免错误使用之前绑定的SqlSession对象,所以要解绑
      local.remove();
      }
      }
      }
    • AccountServiceImpl 中的代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      package com.f.bank.service.impl;

      import com.f.bank.dao.AccountDao;
      import com.f.bank.dao.impl.AccountDaoImpl;
      import com.f.bank.exceptions.MoneyNotEnoughException;
      import com.f.bank.exceptions.TransferException;
      import com.f.bank.pojo.Account;
      import com.f.bank.service.AccountService;
      import com.f.bank.utils.SqlSessionUtil;
      import org.apache.ibatis.session.SqlSession;

      /**
      * @author fzy
      * @date 2024/1/6 11:36
      */
      public class AccountServiceImpl implements AccountService {
      private AccountDao accountDao = new AccountDaoImpl();

      @Override
      public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      // 1.判断转出账户的余额是否充足(select)
      Account fromAct = accountDao.selectByActno(fromActno);
      if (fromAct.getBalance() < money) {
      // 2.如果转出账户的余额不足,提示用户
      throw new MoneyNotEnoughException("您的账户余额不足!");
      }
      try {
      // 3.如果转出账户余额充足,更新转出账户余额(update)
      // 先更新内存中java对象的信息,再更新数据库
      Account toAct = accountDao.selectByActno(toActno);
      fromAct.setBalance(fromAct.getBalance() - money);
      toAct.setBalance(toAct.getBalance() + money);
      int count = accountDao.updateAct(fromAct);
      // 4.更新转入账户余额(update)
      count += accountDao.updateAct(toAct);
      // 提交事务
      sqlSession.commit();
      } catch (Exception e) {
      throw new TransferException("转账失败, 原因未知");
      } finally {
      // 释放资源
      SqlSessionUtil.close(sqlSession);
      }
      }
      }
    • AccountDaoImpl 中的代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      package com.f.bank.dao.impl;

      import com.f.bank.dao.AccountDao;
      import com.f.bank.pojo.Account;
      import com.f.bank.utils.SqlSessionUtil;
      import org.apache.ibatis.session.SqlSession;

      /**
      * @author fzy
      * @date 2024/1/6 11:51
      */
      public class AccountDaoImpl implements AccountDao {
      @Override
      public Account selectByActno(String actno) {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);
      // 不进行commit和close了
      return account;
      }

      @Override
      public int updateAct(Account act) {
      SqlSession sqlSession = SqlSessionUtil.openSession();
      int count = sqlSession.update("account.updateAct", act);
      // 不进行commit和close了
      return count;
      }
      }

6.3 ★Mybatis对象作用域

SqlSessionFactoryBuilder

  • 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。

    因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

  • 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。
---------------The End---------------