13.3 实现DAO持久层

MyBatis建议定义接口完成SQL语句的映射,该接口可以直接作为DAO组件使用。使用DAO模式,既能体现业务逻辑组件封装DAO组件的门面模式,也可分离业务逻辑组件和DAO组件的功能:业务逻辑组件负责业务逻辑的变化,而DAO组件负责持久化技术的变化,这正是桥接模式的应用。

引入DAO模式后,每个DAO组件包含了数据库的访问逻辑:每个DAO组件可对一个数据库表完成基本的CRUD等操作

13.3.1 公共常量类

HrmConstants.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.fkit.hrm.util.common;

public class HrmConstants
{
// 数据库表常量
public static final String USERTABLE = "user_inf";
public static final String DEPTTABLE = "dept_inf";
public static final String JOBTABLE = "job_inf";
public static final String EMPLOYEETABLE = "employee_inf";
public static final String NOTICETABLE = "notice_inf";
public static final String DOCUMENTTABLE = "document_inf";

// 登录
public static final String LOGIN = "loginForm";
// 用户的session对象
public static final String USER_SESSION = "user_session";

// 默认每页4条数据
public static int PAGE_DEFAULT_SIZE = 4;
}

HrmConstants类中定义了本系统中使用的常量。

13.2 数据表和持久化类

通过使用MyBatis持久层,可以避免使用传统的JDBC方式来操作数据库,如此可以使SQL语句更加灵活,并且可以直接通过面向对象的方式操作数据。

13.2.1 设计数据库表

首先,根据模块功能设计数据库表,具体SQL语句如下:

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
-- 创建数据库hrm_db
CREATE DATABASE hrm_db;
-- 使用数据库hrm_db
USE hrm_db;
-- 创建表dept_inf
CREATE TABLE dept_inf (
ID INT(11) NOT NULL AUTO_INCREMENT,
NAME VARCHAR(50) NOT NULL,
REMARK VARCHAR(300) DEFAULT NULL,
PRIMARY KEY (ID)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
INSERT INTO dept_inf(ID,NAME,REMARK) VALUES (1,'技术部','技术部'),(2,'运营部','运营部'),(3,'财务部','财务部'),(5,'总公办','总公办'),(6,'市场部','市场部'),(7,'教学部','教学部');
-- 创建表job_inf
CREATE TABLE job_inf (
ID INT(11) NOT NULL AUTO_INCREMENT,
NAME VARCHAR(50) NOT NULL,
REMARK VARCHAR(300) DEFAULT NULL,
PRIMARY KEY (ID)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
INSERT INTO job_inf(ID,NAME,REMARK) VALUES (1,'职员','职员'),(2,'Java开发工程师','Java开发工程师'),(3,'Java中级开发工程师','Java中级开发工程师'),(4,'Java高级开发工程师','Java高级开发工程师'),(5,'系统管理员','系统管理员'),(6,'架构师','架构师'),(7,'主管','主管'),(8,'经理','经理'),(9,'总经理','总经理');
-- 创建表user_inf
CREATE TABLE user_inf (
ID INT(11) NOT NULL AUTO_INCREMENT,
loginname VARCHAR(20) NOT NULL,
PASSWORD VARCHAR(16) NOT NULL,
USERSTATUS INT(11) NOT NULL DEFAULT '1',
createdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
username VARCHAR(20) DEFAULT NULL,
PRIMARY KEY (ID)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO user_inf(ID,loginname,PASSWORD,USERSTATUS,createdate,username) VALUES (1,'admin','123456',2,'2016-03-12 09:34:28','超级管理员');
-- 创建表employee_inf
CREATE TABLE employee_inf (
ID INT(11) NOT NULL AUTO_INCREMENT,
DEPT_ID INT(11) NOT NULL,
JOB_ID INT(11) NOT NULL,
NAME VARCHAR(20) NOT NULL,
CARD_ID VARCHAR(18) NOT NULL,
ADDRESS VARCHAR(50) NOT NULL,
POST_CODE VARCHAR(50) DEFAULT NULL,
TEL VARCHAR(16) DEFAULT NULL,
PHONE VARCHAR(11) NOT NULL,
QQ_NUM VARCHAR(10) DEFAULT NULL,
EMAIL VARCHAR(50) NOT NULL,
SEX INT(11) NOT NULL DEFAULT '1',
PARTY VARCHAR(10) DEFAULT NULL,
BIRTHDAY DATETIME DEFAULT NULL,
RACE VARCHAR(100) DEFAULT NULL,
EDUCATION VARCHAR(10) DEFAULT NULL,
SPECIALITY VARCHAR(20) DEFAULT NULL,
HOBBY VARCHAR(100) DEFAULT NULL,
REMARK VARCHAR(500) DEFAULT NULL,
CREATE_DATE TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (ID),
KEY FK_EMP_DEPT (DEPT_ID),
KEY FK_EMP_JOB (JOB_ID),
CONSTRAINT FK_EMP_DEPT FOREIGN KEY (DEPT_ID) REFERENCES dept_inf (ID),
CONSTRAINT FK_EMP_JOB FOREIGN KEY (JOB_ID) REFERENCES job_inf (ID)
) ENGINE=INNODB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8;
INSERT INTO employee_inf(ID,DEPT_ID,JOB_ID,NAME,CARD_ID,ADDRESS,POST_CODE,TEL, PHONE,QQ_NUM,EMAIL,SEX,PARTY,BIRTHDAY,RACE,EDUCATION,SPECIALITY,HOBBY,REMARK,CREATE_DATE)
VALUES (1,1,8,'爱丽丝','4328011988','广州天河','510000','020-77777777', '13902001111','36750066','251425887@qq.com',0,'党员','1980-01-01 00:00:00','满','本科','美声','唱歌','四大天王','2016-03-14 11:35:18'),
(2,2,1,'杰克','22623','43234','42427424','42242','4247242','42424',
'251425887@qq.com',2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'2016-03-14 11:35:18'),
(3,1,2,'bb','432801197711251038','广州','510000','020-99999999','13907351532', '36750064','36750064@qq.com',1,'党员','1977-11-25 00:00:00','汉','本科','计算机','爬山','无','2016-07-14 09:54:52');
-- 创建表notice_inf
CREATE TABLE notice_inf (
ID INT(11) NOT NULL AUTO_INCREMENT,
TITLE VARCHAR(50) NOT NULL,
CONTENT TEXT NOT NULL,
CREATE_DATE TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
USER_ID INT(11) DEFAULT NULL,
PRIMARY KEY (ID),
KEY FK_NOTICE_USER (USER_ID),
CONSTRAINT FK_NOTICE_USER FOREIGN KEY (USER_ID) REFERENCES user_inf (ID)
) ENGINE=INNODB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
-- 创建表document_inf
CREATE TABLE document_inf (
ID INT(11) NOT NULL AUTO_INCREMENT,
TITLE VARCHAR(50) NOT NULL,
filename VARCHAR(300) NOT NULL,
REMARK VARCHAR(300) DEFAULT NULL,
CREATE_DATE TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
USER_ID INT(11) DEFAULT NULL,
PRIMARY KEY (ID),
KEY FK_DOCUMENT_USER (USER_ID),
CONSTRAINT FK_DOCUMENT_USER FOREIGN KEY (USER_ID) REFERENCES user_inf (ID)
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

13.1 项目简介及系统结构

人事管理系统是办公信息自动化建设中最常见的项目,几乎每个OA系统中都需要人事管理系统。
该项目包含了用户管理部门管理职位管理员工管理公告管理下载中心等多个模块,页面使用jQuery框架完成动态功能(关于jQuery的知识请参考”疯狂Java系列”之《疯狂前端开发讲义》)。
用户管理、部门管理等模块包含了实际项目开发中常用的增删改查动作,下载中心包含了Spring MVC文件上传文件下载等功能。

13.1.1系统功能介绍

  • 用户管理的功能包括:
    • 添加用户,用户可以为管理员或者普通用户;
    • 查询用户,可以查询所有用户或根据用户名和用户状态进行模糊查询;
    • 删除用户;
    • 修改用户。
  • 部门管理的功能包括:
    • 添加部门;
    • 査询部门,可以査询所有部门或根据部门名称进行模糊査询;
    • 删除部门;
    • 修改部门。
  • 职位管理的功能包括:
    • 添加职位;
    • 査询职位,可以査询所有职位或根据职位名称进行模糊査询:
    • 删除职位;
    • 修改职位。
  • 员工管理的功能包括:
    • 添加员工;
    • 查询员工,可以査询所有员工或根据员工姓名、身份证号、手机号、性别、职位、部门进行模糊查询;
    • 删除员工;
    • 修改员工。
  • 公告管理的功能包括:
    • 添加公告;
    • 查询公告,可以查询所有公告或根据公告名称、公告内容进行模糊查询;
    • 删除公告;
    • 修改公告。
  • 下载中心的功能包括:
    • 上传文件;
    • 查询文件,可以查询所有文件或根据文件标题进行模糊査询;预览文件内容;
    • 删除文件;
    • 下载文件。

所有查询页面统一使用分页处理。

13.1.2 相关技术介绍

本系统主要涉及三个开源框架:MyBatis3Spring5和表现层框架jQuery1.11。同时还使用了JSP作为表现层技术。本系统将这4种技术有机地结合在一起,从而构建出一个健壮的Java EE应用。

1.传统表现层技术:JSP

本系统使用JSP作为表现层,负责收集用户请求数据以及业务数据的表示。
JSP是最传统也最有效的表现层技术。本系统的JSP页面是单纯的表现层,所有的JSP页面不再使用Java脚本。结合EL表达式和JSTL标签库,JSP可完成全部的表现层功能,如数据收集、数据展现。

2.MVC框架

本系统使用Spring MVC作为MVC框架。Spring MVC是一个设计优良的MVC框架,大有取代Struts2之势。本应用的所有用户请求,包括系统的超链接和表单提交等,都不再直接发送到表现层JSP页面,而是必须发送给Spring MVCController,Spring MVC控制所有请求的处理和转发。
通过Controller拦截所有请求有个好处:将所有的JSP页面放入WEB-INF/路径下,可以避免用户直接访问JSP页面,从而提高系统的安全性。
本应用使用基于Spring MVC拦截器的权限控制,应用中控制器没有进行权限检査,但每个控制器都需要检査调用者是否有足够的访问权限,这种通用操作正是Spring MVC拦截器的优势所在。

3. Spring框架的作用

Spring框架是系统的核心部分,Spring提供的IoC容器是业务逻辑组件和DAO组件的工厂,它负责生成并管理这些实例。
借助于Spring的依赖注入,各组件以松耦合的方式组合在一起,组件与组件之间的依赖正是通过Spring的依赖注入管理的。其Service组件和DAO对象都采用面向接口编程的方式,从而降低了系统重构的成本,极好地提高了系统的可维护性、可扩展性。
应用事务采用Spring的声明式事务框架。通过声明式事务,无须将事务策略以硬编码的方式与代码耦合在一起,而是放在配置文件中声明,使业务逻辑组件可以更加专注于业务的实现,从而简化开发。同时,声明式事务降低了不同事务策略的切换代价。

4. MyBatis的作用

MyBatisSQL Mapping功能简化了数据库的访问,并在JDBC层提供了更好的封装。
MyBatis可以优雅及灵活的方式操作数据库,由此开发者可以更灵活地编写SQL语句,满足更好的性能需求,为底层DAO对象的实现提供支持。

13.1.3 系统结构

本系统采用严格的Java EE应用结构,主要有如下几个分层:

  • 表现层。由JSP页面组成。
  • MVC层。使用 Spring MVC技术。
  • 业务逻辑层。主要由Spring Ioc容器管理的业务逻辑组件组成。
  • DAO层。由6个DAO组件组成。
  • 领域对象层。由6个Domain Object对象组成。
  • 数据库服务层。使用MySQL数据库存储持久化数据

本应用中的领域对象实际上只是一些简单的Java Bean类,并未提供任何业务逻辑方法,所有的业务逻辑方法都由系统的业务逻辑组件来提供。这种模式简单、直接,系统分层清晰,比较适用于实际项目开发。

13.1.4 系统的功能模块

本系统可以大致分为6个模块:用户管理部门管理职位管理员工管理公告管理文件下载,其主要业务逻辑通过HrmService业务逻辑组件实现,因此可以使用这个业务逻辑组件来封装DAO组件。

提示
通常建议按细粒度的模块来设计Service组件,让业务逻辑组件作为DAO组件的门面,这符合门面模式的设计。同时让DAO组件负责系统持久化逻辑,可以将系统在持久化技术这个维度上的变化独立出去,而业务逻辑组件负责业务逻辑这个维度的改变。
系统以业务逻辑组件作为DAO组件的门面,封装这些DAO组件,业务逻辑组件底层依赖于这些DAO组件,向上实现系统的业务逻辑功能

本系统主要有如下6个DAO对象:

  • UserDao。提供对user_inf表的基本操作。
  • DeptDao。提供对dept_inf表的基本操作
  • JobDao。提供对job_inf表的基本操作。
  • EmployeeDao。提供对employee_inf表的基本操作。
  • NoticeDao。提供对notice_inf表的基本操作。
  • DocumentDao。提供对document_inf表的基本操作。

本系统还提供一个业务逻辑组件:

  • HrmService。提供所有的业务逻辑功能的实现。

项目结构

展开/折叠
G:\Desktop\随书源码\Spring+Mybatis企业应用实战(第2版)\codes\13\hrmapp
├─src\
│ ├─db.properties
│ ├─log4j.xml
│ └─org\
│   └─fkit\
│     └─hrm\
│       ├─controller\
│       │ ├─DeptController.java
│       │ ├─DocumentController.java
│       │ ├─EmployeeController.java
│       │ ├─FormController.java
│       │ ├─JobController.java
│       │ ├─NoticeController.java
│       │ └─UserController.java
│       ├─dao\
│       │ ├─DeptDao.java
│       │ ├─DocumentDao.java
│       │ ├─EmployeeDao.java
│       │ ├─JobDao.java
│       │ ├─NoticeDao.java
│       │ ├─provider\
│       │ │ ├─DeptDynaSqlProvider.java
│       │ │ ├─DocumentDynaSqlProvider.java
│       │ │ ├─EmployeeDynaSqlProvider.java
│       │ │ ├─JobDynaSqlProvider.java
│       │ │ ├─NoticeDynaSqlProvider.java
│       │ │ └─UserDynaSqlProvider.java
│       │ └─UserDao.java
│       ├─domain\
│       │ ├─Dept.java
│       │ ├─Document.java
│       │ ├─Employee.java
│       │ ├─hrm.mgc
│       │ ├─Job.java
│       │ ├─Notice.java
│       │ └─User.java
│       ├─interceptor\
│       │ └─AuthorizedInterceptor.java
│       ├─service\
│       │ ├─HrmService.java
│       │ └─impl\
│       │   └─HrmServiceImpl.java
│       └─util\
│         ├─common\
│         │ └─HrmConstants.java
│         └─tag\
│           ├─PageModel.java
│           └─PagerTag.java
└─WebContent\
  ├─404.html
  ├─css\
  │ ├─css.css
  │ └─pager.css
  ├─fkjava.ico
  ├─images\
  │ ├─404.jpg
  │ ├─9.png
  │ ├─bt_file.jpg
  │ ├─choose_file.jpg
  │ ├─delete.gif
  │ ├─downLoad.png
  │ ├─left_nav_arrow.gif
  │ ├─left_nav_bg.gif
  │ ├─left_nav_bgshw.gif
  │ ├─left_nav_bottom.gif
  │ ├─left_nav_closed.gif
  │ ├─left_nav_expand.gif
  │ ├─left_nav_top.gif
  │ ├─login_bottom.gif
  │ ├─login_left.gif
  │ ├─login_midbg.gif
  │ ├─login_right.gif
  │ ├─login_top.gif
  │ ├─main_bg.gif
  │ ├─main_locbg.gif
  │ ├─main_locleft.gif
  │ ├─main_locright.gif
  │ ├─none.jpg
  │ ├─pointer.gif
  │ ├─prev.gif
  │ ├─StatBarBg.gif
  │ ├─StatBarL.gif
  │ ├─StatBar_admin.gif
  │ ├─StatBar_time.gif
  │ ├─topbg.gif
  │ ├─top_exit.gif
  │ ├─top_home.gif
  │ ├─top_logo.gif
  │ └─update.gif
  ├─index.jsp
  ├─js\
  │ ├─fkjava_timer.js
  │ ├─jquery-1.11.0.js
  │ ├─jquery-migrate-1.2.1.js
  │ ├─jquery.form.js
  │ ├─ligerUI\
  │ 省略......
  │
  ├─META-INF\
  │ └─MANIFEST.MF
  ├─upload\
  └─WEB-INF\
    ├─applicationContext.xml
    ├─jsp\
    │ ├─dept\
    │ │ ├─dept.jsp
    │ │ ├─showAddDept.jsp
    │ │ └─showUpdateDept.jsp
    │ ├─document\
    │ │ ├─document.jsp
    │ │ ├─showAddDocument.jsp
    │ │ └─showUpdateDocument.jsp
    │ ├─employee\
    │ │ ├─employee.jsp
    │ │ ├─showAddEmployee.jsp
    │ │ └─showUpdateEmployee.jsp
    │ ├─job\
    │ │ ├─job.jsp
    │ │ ├─showAddJob.jsp
    │ │ └─showUpdateJob.jsp
    │ ├─left.jsp
    │ ├─loginForm.jsp
    │ ├─main.jsp
    │ ├─notice\
    │ │ ├─notice.jsp
    │ │ ├─previewNotice.jsp
    │ │ ├─showAddNotice.jsp
    │ │ └─showUpdateNotice.jsp
    │ ├─right.jsp
    │ ├─taglib.jsp
    │ ├─top.jsp
    │ └─user\
    │   ├─showAddUser.jsp
    │   ├─showUpdateUser.jsp
    │   └─user.jsp
    ├─lib\
    │ ├─ant-1.9.6.jar
    │ ├─ant-launcher-1.9.6.jar
    │ ├─asm-5.2.jar
    │ ├─aspectjrt.jar
    │ ├─aspectjtools.jar
    │ ├─aspectjweaver.jar
    │ ├─c3p0-0.9.5.2.jar
    │ ├─cglib-3.2.5.jar
    │ ├─commons-fileupload-1.3.3.jar
    │ ├─commons-io-2.6.jar
    │ ├─commons-logging-1.2.jar
    │ ├─hibernate-c3p0-5.2.10.Final.jar
    │ ├─javassist-3.22.0-CR2.jar
    │ ├─javax.servlet.jsp.jstl-1.2.1.jar
    │ ├─javax.servlet.jsp.jstl-api-1.2.1.jar
    │ ├─log4j-1.2.17.jar
    │ ├─log4j-api-2.3.jar
    │ ├─log4j-core-2.3.jar
    │ ├─mchange-commons-java-0.2.11.jar
    │ ├─mybatis-3.4.5.jar
    │ ├─mybatis-spring-1.3.1.jar
    │ ├─mysql-connector-java-5.1.44-bin.jar
    │ ├─ognl-3.1.15.jar
    │ ├─org.aspectj.matcher.jar
    │ ├─slf4j-api-1.7.25.jar
    │ ├─slf4j-log4j12-1.7.25.jar
    │ ├─spring-aop-5.0.1.RELEASE.jar
    │ ├─spring-aspects-5.0.1.RELEASE.jar
    │ ├─spring-beans-5.0.1.RELEASE.jar
    │ ├─spring-context-5.0.1.RELEASE.jar
    │ ├─spring-context-indexer-5.0.1.RELEASE.jar
    │ ├─spring-context-support-5.0.1.RELEASE.jar
    │ ├─spring-core-5.0.1.RELEASE.jar
    │ ├─spring-expression-5.0.1.RELEASE.jar
    │ ├─spring-instrument-5.0.1.RELEASE.jar
    │ ├─spring-jcl-5.0.1.RELEASE.jar
    │ ├─spring-jdbc-5.0.1.RELEASE.jar
    │ ├─spring-jms-5.0.1.RELEASE.jar
    │ ├─spring-messaging-5.0.1.RELEASE.jar
    │ ├─spring-orm-5.0.1.RELEASE.jar
    │ ├─spring-oxm-5.0.1.RELEASE.jar
    │ ├─spring-test-5.0.1.RELEASE.jar
    │ ├─spring-tx-5.0.1.RELEASE.jar
    │ ├─spring-web-5.0.1.RELEASE.jar
    │ ├─spring-webflux-5.0.1.RELEASE.jar
    │ ├─spring-webmvc-5.0.1.RELEASE.jar
    │ └─spring-websocket-5.0.1.RELEASE.jar
    ├─page.tld
    ├─springmvc-config.xml
    ├─web.xml
    └─xxx.xml

第13章 实战项目 人事管理系统

本章要点

  • 人事管理系统功能模块设计
  • 人事管理系统持久化组件设计
  • 人事管理系统业务逻辑组件设计
  • Spring MVC整合MyBatis的优势

本章将会综合运用前面章节所介绍的知识来开发一个完整的人事管理(HRM)系统。该系统包括:

  • 用户管理
  • 部门管理
  • 职位管理
  • 员工管理
  • 公告管理
  • 下载中心

等常用的人事管理系统功能。
本系统采用前面介绍的 Java EE架构: Spring MVC5+ MyBatis3,该系统架构成熟,性能良好,运行稳定。 SpringIoC容器负责管理业务逻辑组件、持久层组件及控制层件,这样可以充分利用 Spring的依赖注入的优势,进一步增强系统的解耦性,从而高应用的可扩展性,降低系统重构的成本。

11.4 注解使用二级缓存

项目结构

展开/折叠
G:\workspace_web2\MyATwoLevelCacheTest
├─src\
│ ├─db.properties
│ ├─domain\
│ │ └─User.java
│ ├─fractory\
│ │ └─SqlSessionFactoryTools.java
│ ├─log4j.xml
│ ├─mapper\
│ │ └─UserMapper.java
│ ├─mybatis-config.xml
│ ├─tb_user.sql
│ └─test\
│   ├─DeleteUserTest.java
│   └─SelectTest.java
└─WebContent\
  ├─META-INF\
  │ └─MANIFEST.MF
  └─WEB-INF\
    └─lib\
      ├─commons-logging-1.2.jar
      ├─log4j-1.2.17.jar
      ├─log4j-api-2.3.jar
      ├─log4j-core-2.3.jar
      ├─mybatis-3.4.5.jar
      └─mysql-connector-java-5.1.44-bin.jar

数据库表

/MyATwoLevelCacheTest/src/tb_user.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 创建数据库表
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(18) DEFAULT NULL,
`sex` char(2) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
-- 插入数据
INSERT INTO `tb_user` VALUES ('1', '小明', '男', '21');
INSERT INTO `tb_user` VALUES ('2', '小王', '男', '22');
INSERT INTO `tb_user` VALUES ('3', '小丽', '女', '18');
INSERT INTO `tb_user` VALUES ('4', '小芳', '女', '18');
INSERT INTO `tb_user` VALUES ('5', '小王', '男', '22');

mybatis相关配置

db.properties

/MyATwoLevelCacheTest/src/db.properties
1
2
3
4
5
6
7
8
9
#保存为db.properties文件,然后在mybatis-config.xml中通过下面标签引入:
#<properties resource="db.properties"/>
#下面的标签放在mybatis-config.xml文件的environments标签的environment子标签的子标签dataSource标签中
#<property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/>

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis
username=root
password=root

log4j.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration
PUBLIC "-//LOG4J//DTD LOG4J//EN"
"https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd" >
<!-- 请在mybatis-config.xml中配置如下设置 -->
<!-- <settings> -->
<!-- <setting -->
<!-- name="logImpl" -->
<!-- value="log4j"/> -->
<!-- </settings> -->
<log4j:configuration>
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%5p [%t] %m%n" />
</layout>
</appender>
<logger name="mapper.UserMapper">
<level value="DEBUG" />
</logger>
<root>
<level value="ERROR" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>

mybatis-config.xml

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
<?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">
<!-- 该配置文件包含对 MyBatis 系统的核心设置 -->
<configuration>
<properties resource="db.properties" />
<settings>
<setting name="logImpl" value="log4j" />
</settings>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="pooled">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="mapper.UserMapper" />
</mappers>
</configuration>

持久化对象

/MyATwoLevelCacheTest/src/domain/User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package domain;
import java.io.Serializable;
public class User
implements Serializable
{
// 使用缓存需要可序列话
private static final long serialVersionUID = 2667279148834038605L;
private Integer id;
private String name;
private String sex;
private Integer age;
public User()
{}
// 此处省略getter和setter方法,请自己补上
@Override
public String toString()
{
return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}

mapper接口

/MyATwoLevelCacheTest/src/mapper/UserMapper.java
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
package mapper;

import java.util.List;
import org.apache.ibatis.annotations.CacheNamespace;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.cache.decorators.LruCache;
import domain.User;

@CacheNamespace(
eviction = LruCache.class,
flushInterval = 60000,
size = 512,
readWrite = true
)
public interface UserMapper {
/**
* 根据id查询用户信息.
*
* @param id
* @return 用户记录
*/
@Select("select * from tb_user where id=#{id}")
@Options(useCache = true)
User selectUserById(Integer id);

/**
* 查询所有的用户信息.
*
* @return 用户列表
*/
@Select("select * from tb_user")
@Options(useCache = true)
List<User> selectAllUsers();

/**
* 根据id删除用户信息.
*
* @param id 主键
*/
@Delete("delete from tb_user where id=#{id}")
void deleteUserById(Integer id);
}

UserMapper接口中只是将之前写在XML文件当中的二级缓存配置写在了注解当中,其他并无不同。

@CacheNamespace注解

@CacheNamespace注解用来配置二级缓存,如下所示:

1
2
3
4
5
6
@CacheNamespace(
eviction = LruCache.class,
flushInterval = 60000,
size = 512,
readWrite = true
)
  • eviction属性表示要使用的回收策略的class,所有回收策略的类型都位于org.apache.ibatis.cache.decorators包下。
  • flushInterval表示刷新时间间隔,单位为毫秒
  • size表示缓存数目
  • readWrite=true表示只读

SqlSession工厂类

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 fractory;

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

public class SqlSessionFactoryTools {
private static SqlSessionFactory sqlSessionFactory = null;
static
{
try
{
InputStream mybatisConfigXML = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(mybatisConfigXML);

} catch (IOException e)
{
e.printStackTrace();
}
}
public static SqlSession getSqlSession()
{
return sqlSessionFactory.openSession();
}
}

测试类

按id查询SelectTest.java

/MyATwoLevelCacheTest/src/test/SelectTest.java
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
package test;
import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFratoryTools;
import mapper.UserMapper;
public class SelectTest{
public static void main(String[] args)
{
SqlSession sqlSession = null;
try
{
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUserById(1);
System.out.println(user);
// 关闭一级缓存
sqlSession.close();
// 重新获取会话
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 重新获取mapper接口代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println("-----------------------------------------------");
user = userMapper.selectUserById(1);
System.out.println(user);
// 提交事务
sqlSession.commit();
} catch (Exception e)
{
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally
{
// 关闭会话
if(sqlSession != null)
sqlSession.close();
}
}
}

运行效果:

1
2
3
4
5
6
7
8
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user where id=?
DEBUG [main] ==> Parameters: 1(Integer)
DEBUG [main] <== Total: 1
User [id=1, name=小明, sex=男, age=21]
-----------------------------------------------
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.5
User [id=1, name=小明, sex=男, age=21]

在关闭第一个sqlSession时,查询的信息会保存到二级缓存中,重新获取sqlSession,再次执行相同的查询时,由于二级缓存中已经有该数据,所以不需要再次执行sql语句,而是直接从二级缓存中返回数据.

按id删除DeleteUserTest.java

/MyATwoLevelCacheTest/src/test/DeleteUserTest.java
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
package test;

import java.util.List;
import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFactoryTools;
import mapper.UserMapper;

public class DeleteUserTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAllUsers();
users.forEach(user -> System.out.println(" " + user));
System.out.println("----------------------------------------");
// userMapper.deleteUserById(users.get(0).getId());
// sqlSession.commit();
sqlSession.close();
System.out.println("----------------------------------------");
// 再次加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 再次获取mapper接口代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
users = userMapper.selectAllUsers();
users.forEach(user -> System.out.println(" " + user));
// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally {
// 关闭会话
if (sqlSession != null)
sqlSession.close();
}

}
}

运行效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user
DEBUG [main] ==> Parameters:
DEBUG [main] <== Total: 5
User [id=1, name=小明, sex=男, age=21]
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]
----------------------------------------
----------------------------------------
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.5
User [id=1, name=小明, sex=男, age=21]
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]

测试并没有执行删除语句,第二次查询时从二级缓存从返回数据.
取消删除语句的注释:

// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAllUsers();
users.forEach(user-> System.out.println(user));
System.out.println("----------------------------------------");
userMapper.deleteUserById(users.get(0).getId());
sqlSession.commit();
sqlSession.close();
System.out.println("----------------------------------------");
// 再次加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 再次获取mapper接口代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
users = userMapper.selectAllUsers();
users.forEach(user-> System.out.println(user));

再次执行,运行效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user
DEBUG [main] ==> Parameters:
DEBUG [main] <== Total: 5
User [id=1, name=小明, sex=男, age=21]
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]
----------------------------------------
DEBUG [main] ==> Preparing: delete from tb_user where id=?
DEBUG [main] ==> Parameters: 1(Integer)
DEBUG [main] <== Updates: 1
----------------------------------------
DEBUG [main] Cache Hit Ratio [mapper.UserMapper]: 0.0
DEBUG [main] ==> Preparing: select * from tb_user
DEBUG [main] ==> Parameters:
DEBUG [main] <== Total: 4
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]
User [id=5, name=小王, sex=男, age=22]

DML语句会清空缓存

DML语句(insert,update,delete)会清空缓存,所以第二次查询时缓存中没有信息,需要再次执行SQL语句从数据库中查询信息.

11.3 注解调用存储过程

项目结构

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
G:\workspace_web2\MyAProcedureTest
├─src
│ ├─db.properties
│ ├─domain
│ │ └─User.java
│ ├─fractory
│ │ └─SqlSessionFratoryTools.java
│ ├─log4j.xml
│ ├─mapper
│ │ └─UserMapper.java
│ ├─mybatis-config.xml
│ ├─procedure.sql
│ ├─tb_user.sql
│ └─test
│ ├─DeleteTest.java
│ ├─InsertTest.java
│ ├─SelectAllTest.java
│ ├─SelectByIdTest.java
│ └─UpdateTest.java
└─WebContent
├─META-INF
│ └─MANIFEST.MF
└─WEB-INF
└─lib
├─commons-logging-1.2.jar
├─log4j-1.2.17.jar
├─log4j-api-2.3.jar
├─log4j-core-2.3.jar
├─mybatis-3.4.5.jar
└─mysql-connector-java-5.1.44-bin.jar

数据库表

/MyAProcedureTest/src/tb_user.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 创建数据表
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(18) DEFAULT NULL,
`sex` char(2) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
-- 插入数据
INSERT INTO `tb_user` VALUES ('1', '小明', '男', '21');
INSERT INTO `tb_user` VALUES ('2', '小王', '男', '22');
INSERT INTO `tb_user` VALUES ('3', '小丽', '女', '18');
INSERT INTO `tb_user` VALUES ('4', '小芳', '女', '18');

创建存储过程

/MyAProcedureTest/src/procedure.sql
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
-- 创建insert_user存储过程
drop procedure if exists insert_user;
DELIMITER $$
CREATE PROCEDURE insert_user (
OUT v_id int,
in v_name varchar(18),
in v_sex varchar(19),
in v_age int
)
BEGIN
INSERT INTO tb_user (name,sex,age) VALUES (v_name,v_sex,v_age);
SET v_id=LAST_INSERT_ID();
END
$$
DELIMITER ;
-- 创建select_user存储过程
drop procedure if exists select_all_user;
delimiter $$
create procedure select_all_user()
begin
select id,name,sex,age from tb_user;
end
$$
DELIMITER ;
-- 创建select_user_by_id存储过程
drop procedure if exists select_user_by_id;
DELIMITER $$
create procedure select_user_by_id(IN in_id INTEGER)
begin
select id,name,sex,age from tb_user where id=in_id;
end
$$
DELIMITER ;
-- 创建update_user_by_id存储过程
drop procedure if exists update_user_by_id;
delimiter $$
create procedure update_user_by_id (
IN p_id int,
IN p_name varchar(18),
IN p_sex varchar(19),
IN p_age int
)
begin
update tb_user set name=p_name,sex=p_sex,age=p_age
where id=p_id;
end
$$
delimiter ;
-- 创建delete_user_by_id存储过程
drop procedure if exists delete_user_by_id;
delimiter $$
create procedure delete_user_by_id(IN p_id integer)
begin
delete from tb_user where id=p_id;
end
$$
delimiter ;

持久化对象

/MyAProcedureTest/src/domain/User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package domain;
public class User{
private Integer id;
private String name;
private String sex;
private Integer age;
public User()
{
super();
}
// 此处省略getter和setter方法,请自己补上
@Override
public String toString()
{
return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}

mapper接口

/MyAProcedureTest/src/mapper/UserMapper.java
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
package mapper;

import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.mapping.StatementType;
import domain.User;

public interface UserMapper {
/**
* 查询所有的用户信息.
*
* @return 包含所有用户信息的List集合.
*/
@Select("{call select_all_user()}")
@Options(statementType = StatementType.CALLABLE)
List<User> selectAllUser();

/**
* 根据id查询用户信息.
*
* @param id 主键
* @return 该主键对应的用户信息
*/
@Select("{call select_user_by_id(#{id,mode=IN})}")
@Options(statementType = StatementType.CALLABLE)
User selectUserById(Integer id);

/**
* 插入一个用户信息.
*
* @param user 用户对象
* @return 插入用户时,系统分配的主键
*/
@Insert("{call insert_user("
+ "#{id,mode=OUT,jdbcType=INTEGER},"
+ "#{name,mode=IN},"
+ "#{sex,mode=IN},"
+ "#{age,mode=IN}"
+ ")}")
@Options(statementType = StatementType.CALLABLE)
Integer insertUser(User user);

@Update("{call update_user_by_id("
+ "#{id,mode=IN},"
+ "#{name,mode=IN},"
+ "#{sex,mode=IN},"
+ "#{age,mode=IN}"
+ ")}")
@Options(statementType = StatementType.CALLABLE)
void updateUserById(User user);

/**
* 根据id删除用户.
*
* @param id 用户的主键
*/
@Delete("{call delete_user_by_id(#{id,mode=IN})}")
@Options(statementType = StatementType.CALLABLE)
void deleteUserById(Integer id);
}

UserMapper.java只是将之前写在XML文件当中调用存储过程的SQL语句写在了注解当中,注意需要使用@Options(statementType=StatementType.CALLABLE)提供调用存储过程的CALLBALE选项,其他并无不同。

调用存储过程select

查询全部

存储过程如下:

1
2
3
4
5
6
7
8
9
-- 创建select_user存储过程
drop procedure if exists select_all_user;
delimiter &&
create procedure select_all_user()
begin
select id,name,sex,age from tb_user;
end
&&
DELIMITER ;

查询所有的mapper接口方法如下:

1
2
3
@Select("{call select_all_user()}")
@Options(statementType=StatementType.CALLABLE)
List<User> selectAllUser();

select_all_user这个存储过程没有参数,直接在前面加上call调用即可,注意存储过程时不要忘了加上括号,也就是:call 存储过程名称 括号,
还有就是放在注解中的存储过程调用语句要用大括号包裹起来,至于为什么,我还不知道,先这样背着.

SelectAllTest.java

/MyAProcedureTest/src/test/SelectAllTest.java
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
package test;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFratoryTools;
import mapper.UserMapper;
public class SelectAllTest{
public static void main(String[] args)
{
SqlSession sqlSession = null;
try
{
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAllUser();
System.out.println("________________________________");
users.forEach(user-> System.out.println(user));
// 提交事务
sqlSession.commit();
} catch (Exception e)
{
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally
{
// 关闭会话
if(sqlSession != null)
sqlSession.close();
}
}
}

运行效果

1
2
3
4
5
6
7
8
9
DEBUG [main] ==>  Preparing: {call select_all_user()} 
DEBUG [main] ==> Parameters:
DEBUG [main] <== Total: 4
DEBUG [main] <== Updates: 0
________________________________
User [id=1, name=小明, sex=男, age=21]
User [id=2, name=小王, sex=男, age=22]
User [id=3, name=小丽, sex=女, age=18]
User [id=4, name=小芳, sex=女, age=18]

根据id查询

用到的存储过程如下:

1
2
3
4
5
6
7
8
9
-- 创建select_user_by_id存储过程
drop procedure if exists select_user_by_id;
DELIMITER &&
create procedure select_user_by_id(IN in_id INTEGER)
begin
select id,name,sex,age from tb_user where id=in_id;
end
&&
DELIMITER ;

mapper接口方法如下:

1
2
3
@Select("{call select_user_by_id(#{id,mode=IN})}")
@Options(statementType=StatementType.CALLABLE)
User selectUserById(Integer id);

注意存储过程的参数写法,#{id,mode=IN}这个是mybatis的表达式,该表达式中第一项id是参数名称,第二项mode=IN表示这个参数是存储过程的输入参数。

SelectByIdTest.java

/MyAProcedureTest/src/test/SelectByIdTest.java
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
package test;
import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFratoryTools;
import mapper.UserMapper;
public class SelectByIdTest{
public static void main(String[] args)
{
SqlSession sqlSession = null;
try
{
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUserById(1);
System.out.println("_______________________________________________");
System.out.println(user);
// 提交事务
sqlSession.commit();
} catch (Exception e)
{
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally
{
// 关闭会话
if(sqlSession != null)
sqlSession.close();
}
}
}

运行效果如下:

1
2
3
4
5
6
DEBUG [main] ==>  Preparing: {call select_user_by_id(?)} 
DEBUG [main] ==> Parameters: 1(Integer)
DEBUG [main] <== Total: 1
DEBUG [main] <== Updates: 0
_______________________________________________
User [id=1, name=小明, sex=男, age=21]

调用存储过程update

对应的存储过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 创建update_user_by_id存储过程
drop procedure if exists update_user_by_id;
delimiter &&
create procedure update_user_by_id (
IN p_id int,
IN p_name varchar(18),
IN p_sex varchar(19),
IN p_age int
)
begin
update tb_user set name=p_name,sex=p_sex,age=p_age
where id=p_id;
end
&&
delimiter ;

mapper接口方法如下:

1
2
3
4
5
6
7
8
@Update("{call update_user_by_id("
+ "#{id,mode=IN},"
+ "#{name,mode=IN},"
+ "#{sex,mode=IN},"
+ "#{age,mode=IN}"
+ ")}")
@Options(statementType=StatementType.CALLABLE)
void updateUserById(User user);

UpdateTest.java

/MyAProcedureTest/src/test/UpdateTest.java
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 test;
import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFratoryTools;
import mapper.UserMapper;
public class UpdateTest{
public static void main(String[] args)
{
SqlSession sqlSession = null;
try
{
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectUserById(3);
System.out.println("________________________________");
if(user != null)
{
System.out.println("更新之前的数据:" + user);
System.out.println("________________________________");
user.setName("李四");
user.setSex("男");
user.setAge(22);
userMapper.updateUserById(user);
System.out.println("________________________________");
user = userMapper.selectUserById(user.getId());
System.out.println("________________________________");
System.out.println("更新之后的数据:" + user);
}
// 提交事务
sqlSession.commit();
} catch (Exception e)
{
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally
{
// 关闭会话
if(sqlSession != null)
sqlSession.close();
}
}
}

运行结果如下:

DEBUG [main] ==>  Preparing: {call select_user_by_id(?)} 
DEBUG [main] ==> Parameters: 3(Integer)
DEBUG [main] <==      Total: 1
DEBUG [main] <==    Updates: 0
________________________________
更新之前的数据:User [id=3, name=小丽, sex=女, age=18]
________________________________
DEBUG [main] ==>  Preparing: {call update_user_by_id(?,?,?,?)} 
DEBUG [main] ==> Parameters: 3(Integer), 李四(String), 男(String), 22(Integer)
DEBUG [main] <==    Updates: 1
________________________________
DEBUG [main] ==>  Preparing: {call select_user_by_id(?)} 
DEBUG [main] ==> Parameters: 3(Integer)
DEBUG [main] <==      Total: 1
DEBUG [main] <==    Updates: 0
________________________________
更新之后的数据:User [id=3, name=李四, sex=男, age=22]

调用存储过程delete

测试的存储过程如下:

1
2
3
4
5
6
7
8
9
-- 创建delete_user_by_id存储过程
drop procedure if exists delete_user_by_id;
delimiter &&
create procedure delete_user_by_id(IN p_id integer)
begin
delete from tb_user where id=p_id;
end
&&
delimiter ;

对应的mapper接口方法如下:

1
2
3
@Delete("{call delete_user_by_id(#{id,mode=IN})}")
@Options(statementType=StatementType.CALLABLE)
void deleteUserById(Integer id);

DeleteTest.java

/MyAProcedureTest/src/test/DeleteTest.java
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 test;

import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFactoryTools;
import mapper.UserMapper;

public class DeleteTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 查找要删除的记录
User user = userMapper.selectUserById(2);
// 如果找到要删除的记录
if (user != null) {
System.out.println("_________________________________________");
System.out.println("即将删除用户:" + user);
System.out.println("_________________________________________");
// 删除掉该用户
userMapper.deleteUserById(user.getId());
}
// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally {
// 关闭会话
if (sqlSession != null)
sqlSession.close();
}
}
}

运行效果:

1
2
3
4
5
6
7
8
9
10
DEBUG [main] ==>  Preparing: {call select_user_by_id(?)} 
DEBUG [main] ==> Parameters: 2(Integer)
DEBUG [main] <== Total: 1
DEBUG [main] <== Updates: 0
_________________________________________
即将删除用户:User [id=2, name=小王, sex=男, age=22]
_________________________________________
DEBUG [main] ==> Preparing: {call delete_user_by_id(?)}
DEBUG [main] ==> Parameters: 2(Integer)
DEBUG [main] <== Updates: 1

调用存储过程insert

用到的存储过程定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 创建insert_user存储过程
drop procedure if exists insert_user;
DELIMITER &&
CREATE PROCEDURE insert_user (
OUT v_id int,
in v_name varchar(18),
in v_sex varchar(19),
in v_age int
)
BEGIN
INSERT INTO tb_user (name,sex,age) VALUES (v_name,v_sex,v_age);
SET v_id=LAST_INSERT_ID();
END
&&
DELIMITER ;

对应的mapper接口的方法:

1
2
3
4
5
6
7
8
@Insert("{call insert_user("
+ "#{id,mode=OUT,jdbcType=INTEGER},"
+ "#{name,mode=IN},"
+ "#{sex,mode=IN},"
+ "#{age,mode=IN}"
+ ")}")
@Options(statementType=StatementType.CALLABLE)
Integer insertUser(User user);

InsertTest.java

/MyAProcedureTest/src/test/InsertTest.java
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
package test;

import org.apache.ibatis.session.SqlSession;
import domain.User;
import fractory.SqlSessionFactoryTools;
import mapper.UserMapper;

public class InsertTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 获取mapper接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 创建插入的数据
User user = new User();
user.setName("小张");
user.setSex("男");
user.setAge(22);
// 插入数据
userMapper.insertUser(user);
System.out.println("______________________________________");
System.out.println("生成的主键:" + user.getId());
System.out.println("______________________________________");
User user1 = userMapper.selectUserById(user.getId());
System.out.println("插入后的记录:" + user1);

// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally {
// 关闭会话
if (sqlSession != null)
sqlSession.close();
}

}
}

运行效果如下:

1
2
3
4
5
6
7
8
9
10
11
DEBUG [main] ==>  Preparing: {call insert_user(?,?,?,?)} 
DEBUG [main] ==> Parameters: 小张(String), 男(String), 22(Integer)
DEBUG [main] <== Updates: 1
______________________________________
生成的主键:5
______________________________________
DEBUG [main] ==> Preparing: {call select_user_by_id(?)}
DEBUG [main] ==> Parameters: 5(Integer)
DEBUG [main] <== Total: 1
DEBUG [main] <== Updates: 0
插入后的记录:User [id=5, name=小张, sex=男, age=22]

11.2 基于注解的动态SQL4 delete

Provider类中定义生成delete语句的方法

/MyADynamicSQLTest/src/mapper/EmployeeDynamicSQLProvider.java
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
public String deleteEmployeeByPO(Employee parm)
{
return new SQL(){
{
// 在这里写上动态SQL的生成逻辑...
DELETE_FROM("tb_employee");
if (parm.getId() != null)
{
WHERE("id=#{id}");
}
if (parm.getLoginname() != null)
{
WHERE("loginname=#{loginname}");
}
if (parm.getPassword() != null)
{
WHERE("password=#{password}");
}
if (parm.getName() != null)
{
WHERE("name=#{name}");
}
if (parm.getSex() != null)
{
WHERE("sex=#{sex}");
}
if (parm.getAge() != null)
{
WHERE("age=#{age}");
}
if (parm.getPhone() != null)
{
WHERE("phone=#{phone}");
}
if (parm.getSal() != null)
{
WHERE("sal=#{sal}");
}
if (parm.getState() != null)
{
WHERE("state=#{state}");
}
}
}.toString();
}

mapper接口中使用@DeleteProvider引用动态delete语句方法

/MyADynamicSQLTest/src/mapper/EmployeeMapper.java
1
2
3
4
5
@DeleteProvider(
type = EmployeeDynamicSQLProvider.class,
method = "deleteEmployeeByPO"
)
void deleteEmployeeByPO(Employee employee);

测试类

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
package test;

import org.apache.ibatis.session.SqlSession;
import domain.Employee;
import fractory.SqlSessionFactoryTools;
import mapper.EmployeeMapper;

public class DeleteTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 获取mapper接口代理对象
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 创建一个新的对象保存要删除的条件
Employee delCondition = new Employee();
// 添加条件
delCondition.setId(1);
// employee.setLoginname();
// employee.setPassword();
// employee.setName();
delCondition.setSex("女");
// employee.setAge(18);
// employee.setPhone();
// employee.setSal();
// employee.setState();
employeeMapper.deleteEmployeeByPO(delCondition);
// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally {
// 关闭会话
if (sqlSession != null)
sqlSession.close();
}

}
}

运行结果

DEBUG [main] ==>  Preparing: DELETE FROM tb_employee WHERE (id=? AND sex=?) 
DEBUG [main] ==> Parameters: 1(Integer), 女(String)
DEBUG [main] <==    Updates: 0

11.2 基于注解的动态SQL3 update

Provider类中添加生成动态SQL的方法

/MyADynamicSQLTest/src/mapper/EmployeeDynamicSQLProvider.java
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
public String updateEmployeeByPO(Employee employee)
{
return new SQL(){
{
// 在这里写上动态SQL的生成逻辑...
UPDATE("tb_employee");
if(employee.getLoginname() != null)
{
SET("loginname=#{loginname}");
}
if(employee.getPassword() != null)
{
SET("password=#{password}");
}
if(employee.getName() != null)
{
SET("name=#{name}");
}
if(employee.getSex() != null)
{
SET("sex=#{sex}");
}
if(employee.getAge() != null)
{
SET("age=#{age}");
}
if(employee.getPhone() != null)
{
SET("phone=#{phone}");
}
if(employee.getSal() != null)
{
SET("sal=#{sal}");
}
if(employee.getState() != null)
{
SET("state=#{state}");
}
WHERE("id=#{id}");
}
}.toString();
}

mapper接口中引用生成动态update的方法

/MyADynamicSQLTest/src/mapper/EmployeeMapper.java
1
2
3
4
5
@UpdateProvider(
type = EmployeeDynamicSQLProvider.class,
method = "updateEmployeeByPO"
)
void updateEmployeeByPO(Employee employee);

测试类UpdateTest.java

/MyADynamicSQLTest/src/test/UpdateTest.java
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
package test;

import org.apache.ibatis.session.SqlSession;
import domain.Employee;
import fractory.SqlSessionFactoryTools;
import mapper.EmployeeMapper;

public class UpdateTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 查找要修改的行
Employee employee = employeeMapper.selectEmployeeById(3);
System.out.println("-------------------------------------------------");
// 如果找到
if (employee != null) {
System.out.println("修改之前的员工信息");
System.out.println(employee);
System.out.println("-------------------------------------------------");
// 创建一个新对项,保存更新后的信息
Employee employeeUpdate = new Employee();
// 主键id不更新
employeeUpdate.setId(employee.getId());
// 更新其他信息
// employeeUpdate.setLoginname();
// employeeUpdate.setPassword();
employeeUpdate.setName("修改之后的姓名");
employeeUpdate.setSex("男");
// employeeUpdate.setAge(15);
// employeeUpdate.setPhone();
// employeeUpdate.setSal(123.1);
// employeeUpdate.setState("员工");
// 执行更新语句
employeeMapper.updateEmployeeByPO(employeeUpdate);
System.out.println("-------------------------------------------------");
// 再吃查询
employee = employeeMapper.selectEmployeeById(3);
System.out.println("-------------------------------------------------");
// 查看是否更新成功
System.out.println(employee);
}
sqlSession.commit();
} catch (Exception e) {
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally {
// 关闭会话
if (sqlSession != null)
sqlSession.close();
}
}
}

运行效果

DEBUG [main] ==>  Preparing: select * from tb_employee where id=? 
DEBUG [main] ==> Parameters: 3(Integer)
DEBUG [main] <==      Total: 1
-------------------------------------------------
修改之前的员工信息
Employee [id=3, loginname=xiaoli, password=xiaoli, name=小丽, sex=女, age=23, phone=123456789123, sal=7800.0, state=active]
-------------------------------------------------
DEBUG [main] ==>  Preparing: UPDATE tb_employee SET name=?, sex=? WHERE (id=?) 
DEBUG [main] ==> Parameters: 修改之后的姓名(String), 男(String), 3(Integer)
DEBUG [main] <==    Updates: 1
-------------------------------------------------
DEBUG [main] ==>  Preparing: select * from tb_employee where id=? 
DEBUG [main] ==> Parameters: 3(Integer)
DEBUG [main] <==      Total: 1
-------------------------------------------------
Employee [id=3, loginname=xiaoli, password=xiaoli, name=修改之后的姓名, sex=男, age=23, phone=123456789123, sal=7800.0, state=active]

动态生成基于注解形式的insert语句

Provider类中添加生成动态insert的方法

/MyADynamicSQLTest/src/mapper/EmployeeDynamicSQLProvider.java
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
public String insertEmployeeWithParamPO(Employee employee)
{
return new SQL() {
{
// 在这里写上动态SQL的生成逻辑...
INSERT_INTO("tb_employee");
if (employee.getId() != null)
{
VALUES("id", "#{id}");
}
if (employee.getLoginname() != null)
{
VALUES("loginname", "#{loginname}");
}
if (employee.getPassword() != null)
{
VALUES("password", "#{password}");
}
if (employee.getName() != null)
{
VALUES("name", "#{name}");
}
if (employee.getSex() != null)
{
VALUES("sex", "#{sex}");
}
if (employee.getAge() != null)
{
VALUES("age", "#{age}");
}
if (employee.getPhone() != null)
{
VALUES("phone", "#{phone}");
}
if (employee.getSal() != null)
{
VALUES("sal", "#{sal}");
}
if (employee.getState() != null)
{
VALUES("state", "#{state}");
}
}
}.toString();
}

mapper接口中引用动态insert方法

G:\workspace_web2\MyADynamicSQLTest\src\mapper\EmployeeMapper.java
1
2
3
4
5
6
7
8
9
10
@InsertProvider(
type = EmployeeDynamicSQLProvider.class,
method = "insertEmployeeWithParamPO"
)
// 设置使用自动增长主键,主键返回给po类的id属性
@Options(
useGeneratedKeys = true,
keyProperty = "id"
)
int insertEmployeeWithParmPO(Employee employee);

这里使用了两个注解,一个注解是@InsertProvider注解,该注解的type属性支持生成动态SQL的类,method属性指出生成动态SQL的具体方法。
@Options注解的useGeneratedKeys属性表示是否使用自动增长的主键,keyProperty属性表示用于持久化对象接收生成的主键的属性。

测试类insertTest.java

/MyADynamicSQLTest/src/test/insertTest.java
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
package test;

import org.apache.ibatis.session.SqlSession;
import domain.Employee;
import fractory.SqlSessionFactoryTools;
import mapper.EmployeeMapper;

public class insertTest {
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
// 加载mybatis-config.xml,获取SqlSession实例
sqlSession = SqlSessionFactoryTools.getSqlSession();
// 获取mapper接口代理对象
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
// employee.setLoginname("xiaozhang");
// employee.setPassword("xiaozhang");
employee.setName("小张");
employee.setSex("男");
employee.setAge(22);
employee.setPhone("123456789123");
employee.setSal(6800.0);
employee.setState("经理");
employeeMapper.insertEmployeeWithParmPo(employee);
System.out.println("生成的主键:" + employee.getId());
// 提交事务
sqlSession.commit();
} catch (Exception e) {
// 出错回滚事务
sqlSession.rollback();
e.printStackTrace();
} finally {
// 关闭会话
if (sqlSession != null)
sqlSession.close();
}

}
}

运行该测试类,给持久化对象的一些属性赋值,然后调用mapper接口的方法把信息插入到数据库中。mybatis会根据参数设置的数据动态生成values子句.运行效果如下所示:

DEBUG [main] ==>  Preparing: INSERT INTO tb_employee (name, sex, age, phone, sal, state) VALUES (?, ?, ?, ?, ?, ?) 
DEBUG [main] ==> Parameters: 小张(String), 男(String), 22(Integer), 123456789123(String), 6800.0(Double), 经理(String)
DEBUG [main] <==    Updates: 1
生成的主键:5