本文章主要围绕 J2EE 中 SSH ( Spring、Struts、Hibernate ) 框架的配置以及使用问题展开学习的,最终目的是输出可复用的版本,以供后续的项目复用。当然,学习和配置的过程难免有不恰当或错误之处,还望朋友指出、斧正。
教学资源
- 视频 | 黑马程序员. J2EE 进阶. 4 天精通 Hibernate 框架. 2017. bilibili.com
- 视频 | 黑马程序员. J2EE 进阶. 60 集精通 Spring 框架. 2017. bilibili.com
视频 | 黑马程序员. J2EE 进阶. Struts2 框架精品教程. 2017. bilibili.com
本框架的学习笔记是基于此系列教学视频所得的。
更新进度
- 2018.07.17:完成初稿,IDE 搭建、框架初识章节;
- 2018.07.20:更新 SSH 框架 Hibernate 的内容;
- 2018.07.31:更新 SSH 框架 Spring 的内容;
- 2018.08.03:整合 SSH 框架 ( 输出基本框架 );
- 2018.08.08:补充 SSH 框架 Struts 部分的内容;
- 2018.08.15:重新整理 SSH 框架项目,见版本信息;
IDE 搭建
- Step.01:安装 JDK;
- Step.02:安装 IDE (
IntelliJ Idea
/ Eclipse / MyEclipse ); Step.03:配置 Tomcat 环境;
让 Tomcat 可同时运行多个 Module,即在菜单栏
Run > Run Configurations > Deployment > Application context
下配置目录访问路径即可 ( 一般规范填写项目名 )。图 4-1 Tomcat 9.0.x Deployment 紧接着,在菜单栏
File > Project Structure > Artifacts
下配置,把对应的 module 的 Available ElementsPut into Output Root
到xxx:war_exploded
下。图 4-2 Project Structure Artifacts
Step.04:配置 MySQL / SQL Server 数据库;
- 命名规范:数据库
db_dbname
;数据表t_tablename
;属性attribute
访问外网:若在虚拟机或者本机测试,让数据库允许外网访问,注意得关闭防火墙或添加规则允许某端口的访问权限。
Case.01:本机和服务器端互相
Ping
对方 IP,以检验网络是否畅通、是否拒绝访问。
Case.02:若网络畅通、没有拒绝访问,还要留意对应端口是否有权限访问,通过nc -vz IP 端口
检验,返回Succeeded
即成功。数据库权限:若 MySQL 通过命令安装,需特别注意的权限问题,即账号和密码是默认配置的,用户可自行修改,具体修改细节见参考 [6] - [8] 中的方法 ( 可能实操过程中还会遇到其他问题,推荐翻阅下参考文章,也许对你有帮助 )。
若有需求远程连接数据库,我们需要创建一个新用户,并赋予访问权限 (当然,你可让你的 root 账户也赋予远程连接数据库的权限)。具体地:
grant all privileges on *.* to kofe@"%" identified by "123321" with grant option;
.
授权完成后,刷新下账户权限即可:flush privileges;
。
- 命名规范:数据库
Step.05:以 IntelliJ Idea IDE 为参考搭建框架,参考 [1] - [3] 中的方法;
- [1] 中
全注解方式的 SSH 基础框架
的框架整合的思想不错,推荐在你搭建框架时参详。 - [2] - [3] 的搭建方法都是
导 Jar 包形式
,但千万要注意相对应的 Jar 包是否已经下载到本地。 - 当然,推荐
Maven
管理项目,即它是添加依赖库的方式配置环境的,下述的内容也是基于 Maven 构建框架。
- [1] 中
版本信息
基于以下版本,打包的 SSH 框架 ( Maven ):SSHFrameComposition.2018.08.15
这是基于 Maven 构建的 SSH 框架项目,若有错误的地方还望指正。
框架版本:
- Struts:2.5.16
- Spring:4.3.x
- Hibernate:5.2.x
- 其他组件:
- MySQL:5.7.x / SQL Server:2008 R2
- Tomcat:9.0.x
- Maven:3.3.9
- JDK:1.8
构建框架,还需要相关依赖库 ( Jar 包 ),详细请参阅 IoC 入门案例。
为便于你下载 Jar 包或校对依赖是否齐全,具体地,以下列举了 SSH 框架所需要的依赖库。
Spring | Struts2 | Hibernate |
---|---|---|
spring-core | asm | antlr |
spring-beans | asm-commons | stax2-api |
spring-context | asm-tree | geronimo-jta_1.1_spec |
spring-expression | commons-fileupload | hibernate-commons-annotation |
spring-web ( 整合 Web ) | commons-io | hibernate-core |
spring-aop ( 整合 Aop ) | commons-lang3 | hibernate-jpa |
aopalliance ( 整合 Aop ) | freemarker | jandex |
spring-aspect ( 整合 Aop ) | javassist | javassist |
aspectjweaver ( 整合 Aop ) | log4j-api | jboss-logging |
spring-orm | log4j-core | |
spring-tx ( 整合事务 ) | ognl | |
spring-jdbc ( 整合 Hibernate ) | struts-core | |
c3p0 ( 数据库连接池 ) |
其他依赖库 ( Jar 包 ) | 备注 |
---|---|
junit | Junit 单元测试 |
mysql-connector-java | 添加 MySQL 数据库支持 |
struts2-spring-plugin | Struts 整合 Spring 插件 |
struts2-junit-plugin | 便于浏览项目中所有 action 及其与 Jsp View 的映射 |
jstl | JSTL 标签库 |
taglibs-standard-impl | 标签库 |
框架初识
ORM 框架
- 对象关系映射(Object Relational Mapping,O/R Mapping,ORM)是通过使用描述对象和数据库之间映射的
元数据
,将面向对象语言程序中的对象
自动持久化
到关系数据库
中。本质上就是将数据从一种形式转换到另外一种形式。 - 让
实体类
和数据库表
进行一一对应关系 (映射关系),实体类属性
和表里面的字段
对应。操作表对应实体类对象,而不需操作数据库表。
SSH 框架
SSH 框架:Spring、Struts2、Hibernate
集成 SSH 框架的系统从职责上分为四层:表示层
、业务逻辑层
、数据持久层
和 域模块层
,以帮助开发人员在短期内搭建结构清晰、可复用性好、维护方便的 Web 应用程序。其中使用 Struts
作为 系统的整体基础架构
,负责 MVC 的分离,在 Struts 框架的模型部分,控制业务跳转,利用 Hibernate
框架对 持久层
提供支持,Spring
做管理,管理 Struts 和 Hibernate
。
具体做法是:用面向对象的分析方法根据需求提出一些模型,将这些模型实现为基本的 Java 对象,然后编写基本的 DAO (Data Access Objects) 接口,并给出 Hibernate 的 DAO 实现,采用 Hibernate 架构实现的 DAO 类来实现 Java 类与数据库之间的转换和访问,最后由 Spring 做管理,管理 Struts 和 Hibernate。
Struts
主要是用来做 表示层
,也就所谓的 界面
,和用户直接打交道,用来处理用户的请求和请求后返回给用户的模型数据。 Struts 对 Model,View 和 Controller 都提供了对应的组件。
Struts2 入门
Struts2 概述
- Struts2 框架应用在 Java EE 三层结果中的 Web 层框架;
Struts2 解决的问题:
图 6-1 Struts2 解决的问题
Struts2 案例
创建 Action:
1
2
3
4
5
6
7
8
9// BaseAction.java
public class BaseAction extends ActionSupport {
// 每次访问 action ,默认执行名称 execute() 方法
public String execute() throws Exception {
return NONE;
}
}
配置 Action 类访问路径:需在 Src 根目录下创建核心配置文件 struts.xml,其名称和位置是固定的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<struts>
<package name="sample" extends="struts-default" namespace="/">
<!--
| Name 属性值填写 action 访问的名称,例如 BaseAciton.action
| Class 属性值填写目标 Action 的全路径
-->
<action name="BaseAction" class="cn.kofes.action.BaseAction">/
<result name="success">/jsp/success.jsp</result>
</action>
</package>
</struts>
配置过滤器:在 web.xml 中添加过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 过滤器模块 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Struts2 底层执行过程
如图 6-2 所示,为 Struts 底层执行过程图示。
Struts2 配置
Struts 核心配置文件 ( Struts2.5.x 版本 )
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<!-- Struts 2.5.x 版本的约束 -->
<struts>
<!-- 表单的编码/乱码问题 -->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 指定Struts2处于开发阶段,可以进行调试 -->
<constant name="struts.devMode" value="true" />
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<!--
| Name 属性:唯一标识 Package
| Extends 属性:属性固定的的,即 Package 中配置的类具有 action 功能
| Namespace 属性:与 action 标签名构成访问路径
-->
<package name="sample" namespace="/" extends="struts-default" strict-method-invocation="false">
<!-- Struts2.5.x 版本特征,提升安全性 -->
<global-allowed-methods>regex:.*</global-allowed-methods>
<!--
| Name 属性:唯一标识,与 namespace 构成访问访问路径
| Class 属性:Action 类的全路径
| Method 属性:除了 execute() 方法,若绑定 Action 类其他方法则用此属性
-->
<action name="baseAction" class="cn.kofe.action.baseAction" method="login">
<!--
| 根据 Action 类中方法的返回值,配置到不同的路径里面
| Name 属性:与 Action 类对应的方法的返回值一样
| Type 属性:配置如何到路径中 ( 转发或者重定向 ),默认值为转发
-->
<result name="success">/success.jsp</result>
</action>
<!-- Add actions here -->
</package>
</struts>
Struts2 Action 创建
创建普通类,不继承任何类,不实现任何接口;
创建类,实现 Action 接口;1
2
3
4
5
6public class BaseAction implements Action {
public String execute throws Exception {
return NONE; // SUCCESS、ERROR ( 也可自定义字符串 ) ...
}
}创建类,继承 ActionSupport;
1
2
3
4
5
6
7
8
9public class BaseAction extends ActionSupport {
public String execute throws Exception {
return NONE; // SUCCESS、ERROR ( 也可自定义字符串 ) ...
}
// 自定义方法
public String login() { return SUCCESS; }
}
Struts2 Action 方法访问
使用 action 标签中的 method 属性,在属性里写执行的 action 的方法名称;
1
2
3<package name="sample" namespace="/" extends="struts-default" strict-method-invocation="false">
<action name="login" class="cn.kofes.action.BaseAction" method="login" />
</package>使用通配符方式实现;
1
2
3
4<package name="sample" namespace="/" extends="struts-default" strict-method-invocation="false">
<!-- 例如,访问路径为 http://192.168.x.x/action_login.action -->
<action name="action_*" class="cn.kofes.action.BaseAction" method="{1}" />
</package>动态访问实现;
1
2
3
4
5
6
7
8
9<!-- 动态方法访问 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<!-- strict-method-invocation:是否允许使用通配符 ( 默认开启,不需要关闭 ) -->
<package name="sample" namespace="/" extends="struts-default" strict-method-invocation="true">
<!-- 还可以限制具体哪些方法可以动态访问 -->
<global-allowed-methods>regex:.*</global-allowed-methods>
<action name="login" class="cn.kofes.action.BaseAction" method="login" />
</package>常见错误:
- 若 action 方法有返回值,在配置文件中没有配置,会出现无法找到页面的错误 (404)。
- action 的名称,action 方法的返回值常量,规范使用
驼峰式命名
定义。
Struts2 数据操作
结果页面配置
全局结果页面:若多个 Action,方法的返回值相同,到达页面也是相同,则可使用全局结果页面配置。
1
2
3
4
5<package name="sample" namespace="/" extends="struts-default">
<global-results>
<result name="success">/success.jsp</result>
</global-results>
</package>局部结果页面:若配置了全局和局部结果页面,以局部为准。
1
2
3<package name="sample" namespace="/" extends="struts-default">
<action name="login" class="cn.kofes.action.BaseAction" method="login" />
</package>Result 标签 的 Type 属性 (值):
dispatcher
:默认值,转发操作。redirect
:重定向操作。redirectAction
:重定向到 action。。chain
:转发到 action ( 缓存问题 )
表单数据操作
先构造一个表单:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<!-- login.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SSH_FRAME_COMP</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/form.action" method="post">
Username:<input type="text" name="username" /><br/>
Age:<input type="text" name="age" /><br/>
Email:<input type="text" name="email" /><br/>
Phone:<input type="text" name="phone" /><br/>
<input type="submit" value="Submit" />
</form>
</body>
</html>
<!-- struts.xml -->
<action name="FormOperator" class="cn.kofes.action.FormOperator" method="execute">
<!-- 若返回 "success",则重定向到另一个 action,即执行登录 -->
<result name="success" type="redirectAction">login</result>
</action>action 获取表单提交数据
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
37public class FormOperator extends ActionSupport {
public String execute() throw Exception {
// Case.01.使用 ActionContext 类获取
ActionContext context = ActionContext.getContext();
// key 时表单输入项 Name 的属性值,Value 时输入的值
Map<String, Object> map = context.getParameters();
Set<String> keys = map.keySet();
for(String key : keys) {
// 数组形式,考虑有复选框的情况
Object[] obj = (Object[]) map.get(key);
}
// Case.02.使用 ServletActionContext 类获取
HttpServletRequest request = ServletActionContext.getRequest();
String username = request.getParamter("username");
rerun NONE;
}
}
// Case.03.使用接口注入方式获取
public class FormOperator extends ActionSupport implements ServletRequestAware {
private HttpServletRequest request;
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public String execute() throw Exception {
String username = request.getParamter("username");
return NONE;
}
}
action 操作域对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class FormOperator extends ActionSupport {
public String execute() throw Exception {
// 操作域对象
// Request 域
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("requestKey", "requestValue");
// Session 域
HttpSession session = request.getSession();
session.setAttribute("sessionKey", "sessionValue");
// ServletContext 域
ServletContext context = ServletActionContext.getServletContext();
context.setAttribute("contextKey", "contextValue");
rerun NONE;
}
}struts2 提供获取表单数据方式
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// 原始方式获取表单数据,再封装数据到实体类对象中
User user = new User();
HttpServletRequest request = ServletActionContext.getRequest();
user.setUsername( request.getParamter("username") );
// 属性封装:获取表单数据到属性中 ( Setter 和 Getter 方法补全即可 )
// 在 action 的成员变量位置定义变量 ( 变量名称和表单输入项的 Name 属性值一样 )
private String username;
public void setUsername(String username) { this.username = username }
public String getUsername() { return username; }
// 以此类推...
// 模型驱动封装
// Step.01. 实现接口 ModelDriven
// Step.02. 实现接口的方法 getModel(),再把创建对象返回
// Step.03. 在 action 里『创建』实体类对象
public class FormOperator extends ActionSupport implements ModelDriven<User> {
private User user = new User();
public User getModel() {
return user;
}
public Sring execute() throw EXception {
return NONE;
}
}
// 表达式封装
// Step.01.在 action 中『声明』实体类;
// Step.02.生成实体类变量的 Setter 和 Getter 方法
// Step.03.再表单输入项的 Name 属性值中填写表达式
// 表达式:<input type="text" name="user.username" />
public class FormOperator extends ActionSupport {
private User user;
public void setUser(User user) { this.user = user; }
public User getUser() { return user; }
public Sring execute() throw EXception {
return NONE;
}
}
使用模型驱动和属性封装是注意事项:在同一个 action 中,获取表单数据可以是模型驱动合作和属性封装,但不能同时使用二者。
同时使用只会执行模型驱动
。
表达式封装
和模型驱动
比较:- 相同点:使用表达式封装和模型驱动封装都可把数据封装到实体类对象里。
- 不同点:使用模型驱动只能把数据封装到一个实体类对象里。而表达式封装没有限制,即通过表达式封装可把数据封装到多个、不同的实体类对象里面。
struts2 获取数据封装到集合:
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<!-- 封装数据到 List 集合 -->
<input type="text" name="list[0].username" />
<input type="text" name="list[0].age" />
<input type="text" name="list[1].username" />
<input type="text" name="list[1].age" />
<!-- 封装数据到 Map 集合 -->
<input type="text" name="map['Lucy'].username" />
<input type="text" name="map['Lucy'].age" />
<input type="text" name="map['Mark'].username" />
<input type="text" name="map['Mark'].age" />
<!-- 在实现类中声明 -->
public class FormOperator extends ActionSupport {
private List<User> list;
public void setList(List<User> list) { this.list = list; }
public List<User> list getUser() { return list; }
// private Map<String, User> map;
// public void setMap(Map<String, User> map) { this.map = map }
// public Map<String, User> getMap() { return map; }
@Override
public Sring execute() throw EXception {
return NONE;
}
}
案例实操展示
新建表单操作页面
form.jsp
和数据展示页面info.jsp
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<!-- form.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SSH_FRAME_COMP</title>
</head>
<body>
<form name="loginForm" action="${pageContext.request.contextPath}/form.action" method="post">
Username:<input type="text" name="user.username"/><br/>
Age:<input type="text" name="user.age"/><br/>
Email:<input type="text" name="user.email"/><br/>
Phone:<input type="text" name="user.phone"/><br/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
<!-- info.jsp -->
在 struts.xml 配置文件中添加 action:
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<!-- struts.xml -->
<struts>
<!-- 表单的编码/乱码问题 -->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 动态方法访问 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<package name="sample" namespace="/" extends="struts-default" strict-method-invocation="true">
<global-allowed-methods>regex:.*</global-allowed-methods>
<!-- 使用注解创建 FormOperator 对象,直接引用标签值 -->
<action name="form" class="formOperator" method="saveInfo">
<result name="getinfo" type="redirectAction">info</result>
</action>
<!-- 插入数据后加载数据库 -->
<action name="info" class="formOperator" method="getInfo">
<result name="listinfo">/jsp/info.jsp</result>
</action>
</package>
</struts>
在 Action 中实现逻辑处理类:
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// FormOperator.java
// Action 层:界面交互层
"formOperator") (value=
"prototype") (value =
public class FormOperator extends ActionSupport {
private User user;
// Service 处理业务逻辑
"sampleService") (name =
private BaseService service;
public Sring execute() throw EXception {
return NONE;
}
public String saveInfo() {
service.saveUserInfo(user);
return "info";
}
public String getInfo() {
// 从数据库中加载数据
return NONE;
}
}
// SampleServiceImpl.java
// Servie 层:业务逻辑层
"sampleService") (value =
public class SampleServiceImpl implements BaseService {
"sampleDao") (name =
private BaseDao dao;
public void saveUserInfo(User user) {
if( null != dao.queryOneTuple(user.getClass(), user.getUid()) ) {
dao.updateOneTuple(user);
} else {
dao.insertOneTuple(user);
}
}
}
// SampleDaoImpl.java
// Dao 层:数据持久层
"sampleDao") (value =
public class SampleDaoImpl implements BaseDao {
"hibernateTemplate") (name =
private HibernateTemplate hibernateTemplate;
public void insertOneTuple(Object entity) {
hibernateTemplate.save(entity);
}
public void updateOneTuple(Object entity) {
hibernateTemplate.update(entity);
}
public <T> T queryOneTuple(Class<T> entityClass, Serializable id) {
T temporary = null;
if (null != entityClass) {
temporary = hibernateTemplate.get(entityClass, id);
}
return temporary;
}
}
Struts2 值栈
值栈概念
什么是值栈:
- Struts 里本身提供一种存储机制,类似于
域对象
。即值栈,可存储数据也可读取数据。 - Striuts 把数据放到值栈里,在 Jsp 页面中获取到值栈数据。
- Struts 里本身提供一种存储机制,类似于
Ognl:Web 阶段,EL 表达在 Jsp 中获取域对象中的值。而 Ognl 也是一种表达式。
- Struts 中操作值栈数据,和
Struts 标签
一起使用、操作值栈。 - Ognl 不是 Struts 的一部分,可以单独使用。
- Struts 中操作值栈数据,和
Struts 标签:虽然标签封装了样式,方便操作,但故样式上会有限制。
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<!-- 在对应的 jsp 页面中引入标签库 -->
<%@ taglib uri="/struts-tags" prefix="s" %>
<!-- 查看值栈结构和数据 -->
<s:debug />
<!-- 使用 Ognl + Struts 标签,实现计算字符串长度 -->
<s:property value="'str'.length()" />
<!-- html 表单标签
| 1) form:action、method、enctype
| 2) 输入项:大部分在 input 中封装 type 值实现各种输入项,例如:
| text,普通文本输入项
| password,密码输入项
| radio,单选输入项
| checkbox,复选输入项
| file,文件上传项
| hidden,隐藏项
| button,普通按钮
| submit,提交按钮
| image,图片提交
| reset,重置
| 3) select:下拉输入项
| 4) textarea:文本域
-->
<s:form name="signupForm" action="form.action' method="POST">
<!-- 普通输入项 -->
<s:textfield name="username" label="username" />
<!-- 密码输入项 -->
<s:password name="password" label="password" />
<!-- 单选项 -->
<s:radio list="#{'male':'男', 'female':'女'}" name="gender" label="gender" />
<!-- 多选项 -->
<s:checkboxlist list="{'sleep', 'eat'}" name="interest" label="interest" />
<!-- 下拉输入框 -->
<s:select list="{'str1', 'str2', 'str3'}" name="sample" label="sample" />
<!-- 文件上传项 -->
<s:file name="file" label="file" />
<!-- 文本域 -->
<s:textarea row="10" cols="10" name="description" value="description" />
<!-- 隐藏项 -->
<s:hidden name="hiddenContent" value="hidden content is here" />
<!-- 提交按钮 -->
<s:submit value="Submit" />
</s:form>Servlet 和 Action 区别:
- Servlet:默认在第一次访问时创建,仅创建一次。( 单实例 )
- Action:访问时候创建,且每次访问时都会创建 action 对象。( 多实例 )
获取值栈对象
使用 ActionContext 类的方法获取值栈对象
1
2
3
4
5
6
7
8
9
10
11
public class FormOperator extends ActionSupport {
private User user;
public Sring execute() throw EXception {
ActionContext context = ActionContext().getContext();
ValueStack stack = context.getValueStack();
return NONE;
}
}
值栈内部结构
值栈主要分为两部分:
Root:List 集合
Class Compoundroot extends ArrayList { ... }
Context:Map 集合
Class Compoundroot extends Object implements Map { ... }
图 6-3 Context 存储的对象引用
向值栈存数据
结合
<s:debug />
标签,可浏览值栈的传值情况。
向值栈存数据的多种方式:
- 获取值栈对象,调用值栈对象的 set 方法。
- 获取值栈对象,调用值栈对象的 push 方法。
在 action 定义变量,生产变量的 get 方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 获取值栈对象
ActionContext context = ActionContext.getContext();
ValueStack stack = context.getValueStack();
// 向值栈放字符串
// Case.01
stack.set("username", "AttrSet");
// Case.02
stack.push("PushMethod");
// Case.03.声明变量,生成 Getter 方法
private String str;
public String getStr() { return str; }
str = "SetStrAttr";
// 向值栈放对象
private User user;
public User getUser() { return user; }
// user.setXXX();
// 向值栈放 List 集合
private List<User> list;
public List<User> getList() { return list; }
list = getAllUserInfo(User.class);
从值栈取数据
从值栈获取数据
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<!-- 在对应的 jsp 页面中引入 struts 标签库 -->
<%@ taglib uri="/struts-tags" prefix="s" %>
<!-- 在 Jsp 页面中获取属性值 -->
<s:property value="str" />
<!-- Jsp 页面中获取对象的值 -->
<s:property value="user.username" />
<s:property value="user.email" />
<!-- Jsp 页面中获取 List 集合:方式 1 -->
<s:iterator value="list">
<s:property value="username" />
<s:property value="age" />
<s:property value="email" />
<s:property value="phone" />
</s:iterator>
<!-- Jsp 页面中获取 List 集合:方式 2 -->
<s:iterator value="list" var="user">
<s:property value="#user.username" />
<s:property value="#user.age" />
<s:property value="#user.email" />
<s:property value="#user.phone" />
</s:iterator>
Spring
业务逻辑层
,是一个轻量级的控制反转 (IoC) 和面向切面 (AOP) 的容器框架。
Spring 概念
- 一站式轻量级开源框架,在 Java EE 三层结构中,每一层提供不同的解决技术。
Web 层
:Spring MVCService 层
:IoCDAO 层
:Spring 的 JdbcTemplate
- AOP:
面向切面编程
,扩展功能不是通过修改源码实现的,即通过动态代理技术
,把各类通知 / 增强织入到它所约定的流程当中。事实上,是通过引入其他类的方法来实现的。 - IoC:
控制反转
,例如:对象的创建不通过 new 方式实现,而是通过 Spring 配置创建类对象。
Spring IoC
控制反转 (IoC),是一种通过描述 (在 Java 中可以是 XML 或者注解) 并通过第三方去产生或获取特定对象的方式。
IoC 底层原理
- 使用技术:
- XML 配置文件;
- Dom4j 解析 XML;
- 工厂设计模式;
- 类的反射.
- 代码实现:
- 创建 XML 配置文件,配置要创建对象类。
- 创建工厂类,使用 Dom4j 解析配置文件,通过反射创建类对象。
IoC 入门案例
Maven 中添加依赖库 ( 对应导入 Jar 包 ):
下载 Jar 包:到 Maven Pository 中搜索目标 Jar 包,在具体页面中的 Files 一栏可下载。例如:log4j-core.2.11.0
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262<!-- porm.xml -->
<!-- Version Control of Jar Dependency -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.18.RELEASE</spring.version>
<hibernate.version>5.2.17.Final</hibernate.version>
<struts2.version>2.5.16</struts2.version>
</properties>
<!-- Foundation of SSH ( Spring、Struts 2、Hibernate ) -->
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- 以下依赖包为下文所需要的,为方便起见这里统一配置 -->
<!-- struts2 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts2.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<version>5.2</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.1.15</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<!-- config-browser-plugin 插件方便浏览项目中的所有 Action 及其与 Jsp View 的映射 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-junit-plugin</artifactId>
<version>${struts2.version}</version>
<scope>test</scope>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate.common</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>5.0.4.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
<version>2.0.5.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>stax2-api</artifactId>
<version>3.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jta_1.1_spec</artifactId>
<version>1.1.1</version>
</dependency>
<!-- Combining with SSH ( Spring、Struts 2、Hibernate ) framework -->
<!-- Spring 整合 Hibernate 和事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring 整合 AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.7</version>
</dependency>
<!-- Spring 整合 Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Sturts 2 整合 Spring 框架 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>
<!-- Others -->
<!-- 添加对 MySQL 数据库的支持 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!-- 添加对数据源的支持 -->
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- c3p0数据库连接池的辅助包 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.15</version>
</dependency>
<!-- 日志系统 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.10.0</version>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
创建 Spring 配置文件:在 Src 目录下 (建议),创建 Spring 核心配置文件 applicationContext.xml。
1
2
3
4
5
6
7
8
9
10<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd>
<bean id="operator" class="cn.entity.UserOperator"></bean>
</beans>再配置创建类,对象创建 (方便演示,以单元测试形式呈现):
1
2
3
4
5
6
7
8
9
10
11
12
13// 单元测试类
public class IoCTest() {
public void testUser() {
// Step.01.加载 Spring 配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
// Step.02. 得到配置创建的对象
UserOperator operator =
(UserOperator) context.getBean("operator");
}
}
Spring Bean 管理 (XML)
Bean 标签常用属性
- ID 属性:Bean 标签的名称,要求只含数字和大小写英文字母。
- Class 属性:常见对象所在类的全路径。
- Name 属性:功能和 ID 属性一样,Name 属性可包含特殊字符值。
- Scope 属性:
singleton
:单例的 ( 默认值 )。prototype
:多例的。
Bean 实例化的方式
使用类的无参数构造创建对象
1
2
3public class UserOperator {
public UserOperator() { }
}使用静态工厂创建对象
1
2
3
4
5
6
7public class UserOperatorFactory {
public static UserOperator getOperator() {
String className = "classValue"; // cn.entity.UserOperator
Class clz = Class.forName(className);
return clz.newInstance();
}
}
上述实例化方法在 applicationContext.xml 中的配置:
1
2
3
4
5<!-- 无参数构造创建对象 -->
<bean id="operator" class="cn.kofes.entity.UserOperator" scope="singleton"></bean>
<!-- 静态工厂创建对象 -->
<bean id="operator" class="cn.kofes.entity.UserOperatorFactory" factory-method="getOperator"></bean>
属性注入方式
Set() 方法注入
1
2
3
4
5
6public class UserOperator {
private String operationType;
public void setUsername(String operationType) {
this.operationType = operationType;
}
}有参数构造注入
1
2
3
4
5
6public class UserOperator {
private String operationType;
public UserOperator(String operationType) {
this.operationType = operationType;
}
}
注入
对象属性
类型:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class UserOperator {
private User user;
public void setUser(User user) {
this.user = user;
}
public void operatorTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserOperator operator = (UserOperator) context.getBean("operator");
// TODO
}
}
注入
复杂类型
属性:- 数组
- List 集合
- Map 集合
Properties 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class UserOperatorXML {
private String arrs; // 数组
private List<String> list; // List 集合
private Map<String, String> map; // Map 集合
private Properties properties; // Properties 类型
// Setter() 方法此处省略...
public void operatorTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
UserOperatorXML operator =
(UserOperatorXML) context.getBean("operator");
// TODO
}
}
上述
注入方法
在 applicationContext.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
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<!-- -- 有参数构造注入属性 -- -->
<bean id="operator" class="cn.kofes.entity.UserOperatorXML">
<constructor-arg name="operationType" value="modify" />
</bean>
<!-- -- Set 方法注入属性 -- -->
<bean id="operator" class="cn.kofes.entity.UserOperatorXML">
<property name="operationType" value="modify" />
</bean>
<!-- -- 注入 对象属性 类型 -- -->
<bean id="user" class="cn.kofes.entity.User"></bean>
<bean id="operator" class="cn.kofes.entity.UserOperatorXML">
<!--
| Name 属性值:类里面的定义属性的名称
| Ref 属性:定义属性在 Bean 标签中的 ID 值
-->
<property name="user" ref="user" />
</bean>
<!-- -- 注入 复杂类型 属性 -- -->
<bean id="complex" class="cn.kofes.entity.UserOperatorXML">
<!-- 数组、List 集合适用 -->
<property name="arrs">
<list>
<value>Arrs0</value>
<value>Arrs1</value>
<value>Arrs2</value>
</list>
</property>
<!-- Map 集合 -->
<property name="map">
<map>
<entry key="seq1" value="str1" />
<entry key="seq2" value="str2" />
<entry key="seq3" value="str3" />
</map>
</property>
<!-- Properties:例如我们要配置数据库 -->
<property name="properties">
<props>
<prop key="driverclass">com.mysql.jdbc.Driver</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
Spring DI
IoC 与 DI 的区别
IoC
:控制反转
,把对象创建交给 Spring 进行配置。DI
:依赖注入
,向类中的属性设置属性值。关系:依赖注入不能单独存在,需在 IoC 基础之上完成操作。
Spring Bean 管理 (注解)
- 注解格式:
@注解名称(value="属性值")
或者@注解名称("属性值")
@Component("user")
相当于<bean id="user" class="" />
,其三个衍生注解为:- @Controller:Web 层,相当于 Struts 中的 Action 层。
- @Service:业务层 ,业务逻辑处理。
@Repository:持久层,标注数据访问组件,即 DAO 组件。
功能目前来说是一致的,即创建对象。
@Scope("prototype")
或@Scope("singleton")
:即配置的对象是单实例还是多实例。1
2
3"BaseAction") (
"prototype") (
public class BaseAction { ... }
基本内容
- 使用注解创建对象
- 使用注解注入对象
- xml 和注解方式混合使用
小试牛刀
创建类和方法
1
2
3public class User { /* 省略实体类的属性 */ }
public class Customer { /* 省略实体类的属性 */ }
public class Firm { /* 省略实体类的属性 */ }创建 Spring 配置文件,并引入约束 (在上述 applicationContext.xml 中追加)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- 注解扫描:扫描属性上面的注解 -->
<context:annotation-config/>
<!--
| 注解扫描:到包里扫描类、方法、属性上面的注解,
| 即使用 Annotation 自动注册 Bean
-->
<context:component-scan base-package="cn.kofes"/>
<context:component-scan base-package="cn.kofes.entity"/>
</beans>
注解中创建对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 此方式相当于 <bean id="user" class="cn.kofes.entity.User" />
"user") (value=
public class User {
// 省略实体类的属性...
}
public class UserOperatorAnn {
private User user;
public void operatorTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(user); // 直接调用 user 对象
}
}注解注入属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 实体操作类
public class UserOperatorAnn {
// 相当于 Set 方法注入属性:
// @Autowired 或者 @Resource(name = "user")
private User user;
public void operatorTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(user);
System.out.println(customer);
System.out.println(firm);
}
}
XML 配置文件和注解混合使用
- 创建对象操作使用 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
26/* Start:applicationContext.xml */
<bean id="user" class="cn.kofes.entity.User" />
<bean id="customer" class="cn.kofes.entity.Customer" />
<bean id="firm" class="cn.kofes.entity.Firm" />
/* End:applicationContext.xml */
public class UserOperatorAnn {
// 注解方式注入属性
"user") (name =
private User user;
"customer") (name =
private Customer customer;
"firm") (name =
private Firm firm;
public void operatorTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(user);
System.out.println(customer);
System.out.println(firm);
}
}
Spring AOP
AOP 概述
面向切面编程 (Aspect Oriented Programing,AOP),扩展功能不通过修改代码实现。AOP 采取 横向抽取机制
取代传统 纵向继承体系
重复性代码。
- 纵向继承体系:通过继承获得父类的功能 (方法)。
横向抽取机制:动态代理方式。
- 针对有接口的情况,使用 JDK 动态代理。
针对没有接口情况,使用 Cglib 动态代理。
1
2
3
4
5
6
7
8
9public interface Dao {
public void add();
}
public class DaoImpl implements Dao {
public void add() {
// 具体逻辑
}
}
AOP 相关术语
便于理解,引入实体类进行说明:
1 | public class User { |
连接点 (Join Point):指那些被拦截到的点,在 Spring 这些点指的是方法,因 Spring 只支持方法类型的连接点。
类中有哪些方法可被增强,这些方法称为连接点。
切入点 (Pointcut)
:指我们要对哪些Join Point
进行拦截的定义。类中有很多方法被增强,例如实际操作中,只是增强了类中的 add() 和 update() 方法,即称为切入点。
引介 (Introduction):一种特殊的通知在不修改类代码的前提下, Introduction 可为类动态地添加一些方法或 Field。
通知/增强 (Advice)
:指拦截到Join Point
之后要做的事情就是通知。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知 (切面要完成的功能)。增强的逻辑称为增强,例如扩展日志功能,这个日志功能称为增强。
前置通知,在方法之前执行;后置通知,在方法之后执行;
异常通知,方法出现异常;最终通知,在后置之后执行;
环绕通知,在方法之前和之后执行.目标对象 (Target):代理的目标对象 (要增强的类)。
织入 (Weaving):把增强应用到目标的过程,即把 Advice 应用到 Target 的过程。
切面 (Aspect)
:切入点和通知 (引介) 的结合。把增强应用到具体方法上,此过程称为切面。例如把日志功能写进 add() 方法中。
代理 (Proxy):一个类被 AOP 织入增强后,产生一个结果代理类。
Spring AOP 操作
(1) 基于 AspectJ 的 Spring AOP 操作。
ASpectJ:面向切面的框架,其扩展了 Java 语言。AspectJ 定义了 AOP 语法,故它有一个专门的编译器来生成遵守 Java 字节编码规范的 Class 文件。
AspectJ 并不是 Spring 一部分,和 Spring 一起使用进行 AOP 操作。
使用 AspectJ 实现 AOP 有两种方式:1) 基于 AspectJ 的 XML 配置;2) 基于 AspectJ 的注解方式。
(2) 操作基本流程 ( XML 方式 ):
使用表达式配置切入点
execution( <访问修饰符>?<返回类型><方法名>(<参数>)(异常) )
execution( * cn.kofes.UserOpertor.add(..) ) — UserOperator 类中 add() 方法增强
execution( * cn.kofes.UserOperator.*(..) ) — UserOperator 类中所有方法增强
execution( * *.*(..) ) — 所有类中所有方法增强
execution( * save*(..) ) — 所有 save 开头的方法增强
代码实现
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// strengthenUserOperator.java
public class strengthenUserOperator {
public strengthenUserOperator() { }
/**
* This's an aspect inserting to the method() the front.
*/
public void listBeforeOperatorType() {
System.out.println("Inserting to the method() the front.");
}
/**
* This's an aspect inserting to the method() the front and latter.
* @param proceedingJoinPoint 执行被增强的方法
* @throws Throwable
*/
public void listArroundOperatorType(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Inserting to the method() the front.");
proceedingJoinPoint.proceed();
System.out.println("Inserting to the method() the latter.");
}
}
// UserOperatorXML.java
public class UserOperatorXML {
private User user;
public void setUser(User user) { this.user = user; }
public User getUser() { return user; }
public void operatorTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserOperatorXML operator = (UserOperatorXML) context.getBean("operator");
// 会在用户信息之前输出 “前置增强” 的信息。
System.out.println( operator.getUser() );
}
}创建 Spring 配置文件,并引入约束 (在上述 applicationContext.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
26
27
28
29
30
31
32
33
34<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- 无参数构造创建对象 -->
<bean id="operator" class="cn.kofes.entity.UserOperatorXML" />
<bean id="strengthenoperator" class="cn.kofes.entity.strengthenUserOperator" />
<!-- 配置 AOP 操作 -->
<aop:config>
<!-- 配置切入点:匹配 UserOperator 类中所有方法 -->
<aop:pointcut id="pointcutA" expression="execution( * cn.kofes.UserOperatorXML.*(..))" />
<!-- 配置切面:把增强用到方法上面 -->
<aop:aspect ref="strengthenoperator">
<!-- 配置增强类型
| Aop:增强类型有 before、after、arroud 等
| Method:增强类里面使用哪个方法作为前置
-->
<aop:before method="listAllOperatorType" pointcut-ref="pointcutA" />
</aop:aspect>
</aop:config>
</beans>
(3) 操作基本流程 ( 注解方式 ):
创建 Spring 配置文件 (在上述 applicationContext.xml 中追加),XML 配置创建对象,并开启 AOP 操作:
1
2
3
4
5<!-- 无参数构造创建对象 -->
<bean id="strengthenoperator" class="cn.kofes.entity.strengthenUserOperator" />
<!-- 开启 AOP 操作 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在增强类上面使用注解完成 AOP 操作:
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// strengthenUserOperator.java
public class strengthenUserOperator {
public strengthenUserOperator() { }
/**
* This's an aspect inserting to the method() the front.
*/
public void listBeforeOperatorType() {
System.out.println("Inserting to the method() the front.");
}
/**
* This's an aspect inserting to the method() the front and latter.
* @param proceedingJoinPoint 执行被增强的方法
* @throws Throwable
*/
"execution( * cn.kofes.entity.UserOperatorXML.*(..) )") (value=
public void listArroundOperatorType(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Inserting to the method() the front.");
proceedingJoinPoint.proceed();
System.out.println("Inserting to the method() the latter.");
}
}
Spring 整合 Web 项目
诉求
:在 Spring 运作中,首先加载 Spring 核心配置文件,再创建对象。而创建对象可通过 New 的方式创建,但效率太低,则我们可以把加载配置文件和创建对象过程,在服务器启动时完成。
问题引入
- Action 调用 Service,Service 调用 Dao。而每次 Action 时都会加载 Spring 配置文件,影响性能。
实现原理
- ServletContext 对象
- 监听器 (观察者模式)
操作简叙
- 在服务器启动时,为每个项目创建一 ServletContext 对象;
- 在 ServletContext 对象创建时,使用监听器监听 ServletContext 对象在什么时创建;
- 监听到 ServletContext 对象创建时,加载 Spring 配置文件,把配置文件配对象创建;
- 把创建的对象放置 ServletContext 域对象里;
- 到 ServletContext 域中,通过 getAttribute() 方法获取对象。
具体实现
在
web.xml
配置文件中添加监听器,并指定 Spring 配置文件 的位置。1
2
3
4
5
6
7
8
9
10<!-- 指定 Spring 配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 监听器模块 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Spring JdbcTemplate 操作
- Spring 对不同的持久化层技术都进行了封装:
Jdbc
Hibernate 5.x
iBatis / MyBatis
JPA
- JdbcTemplate 对 Jdbc 进行了封装,以下为实际操作介绍。
JdbcTemplate 增删改查
- Step.01.创建对象,设置数据库信息
- Step.02.创建 jdbcTemplate 对象,设置数据源
Step.03.调用 jdbcTemplate 对象,实现其中的方法实现增、删、改、查操作。
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// Jdbc 模板依赖连接池获得数据库连接,所以必须先构造连接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://192.168.x.x/db_testdb");
dataSource.setUsername("root");
dataSource.setPassword("123456");
// 创建 Jdbc 模板
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 建表 SQL 语句
String sql_create = "CREATE TABLE " +
"t_user(id int primary key auto_increment, username varchar(20), password varchar(20))";
String sql_insert = "INSERT INTO t_user VALUES(?, ?, ?)";
String sql_update = "UPDATE t_user password = ? WHERE username = ?";
String sql_delete = "DELETE FROM t_user WHERE username = ?";
// JdbcTemplate 实现增、删、改操作
jdbcTemplate.execute(sql_create);
jdbcTemplate.update(sql_insert, "Lucy", "123456");
jdbcTemplate.update(sql_update, "Lucy", "abc123");
jdbcTemplate.update(sql_delete, "Lucy");
/**
* JdbcTemplate 实现查询操作,使用 RowMapper 接口,
* 但 JdbcTemplate 没有针对这个接口提供实现类,得到不同的类型数据需要进行数据封装
*/
// 查询返回某一个值
sql_select_certain = "SELECT count(*) FROM t_user";
// args0:SQL 语句,args1:返回类型 Class
jdbcTemplate.queryForObject(sql_select_certain, Integer.class);
// 查询返回 list 集合
sql_select_all = "SELECT * FROM t_user";
List<User> list = jdbcTemplate.query( sql_select_all, new MyRowMapper() );
// 查询返回 list 集合,需要创建类实现 RowMapper 接口
public class MyRowMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int num) throws SQLExpection {
User user = new User();
user.setUsername( rs.getString("username") );
user.setPassword( rs.getString("password") );
return user;
}
}
Spring 配置连接池
创建 Spring 配置文件,配置连接池:
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<!-- 为便于修改配置,可在根目录下新建文件 c3p0.properties,填写格式如下:
| datasource.driverClass=com.mysql.jdbc.Driver
| datasource.jdbcUrl=jdbc:mysql://192.168.x.x:3306/db_testdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
| datasource.user=root
| ...
-->
<context:property-placeholder location="classpath:c3p0.properties"/>
<!-- Data Connection Setting -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 引用 c3p0.properties 的键值对即可 -->
<property name="driverClass" value="${datasource.driverClass}"/>
<property name="jdbcUrl" value="${datasource.jdbcUrl}"/>
<property name="user" value="${datasource.user}"/>
<property name="password" value="${datasource.password}"/>
<!-- 设置数据库连接池的最大连接数 -->
<property name="maxPoolSize" value="${datasource.maxPoolSize}"/>
<!-- 设置数据库连接池的最小连接数 -->
<property name="minPoolSize" value="${datasource.minPoolSize}"/>
<!-- 设置数据库连接池的初始化连接数 -->
<property name="initialPoolSize" value="${datasource.initialPoolSize}"/>
<!-- 设置数据库连接池的连接最大空闲时间 -->
<property name="maxIdleTime" value="${datasource.maxIdleTime}"/>
<!-- c3p0缓存Statement的数量数 -->
<property name="maxStatements" value="${datasource.maxStatements}"/>
<!-- 当连接池的连接用完的,从 C3p0 下获取新的连接数 -->
<property name="acquireIncrement" value="${datasource.acquireIncrement}"/>
<property name="checkoutTimeout" value="${datasource.checkoutTimeout}"/>
<property name="idleConnectionTestPeriod" value="${datasource.idleConnectionTestPeriod}"/>
</bean>
<!-- JdbcTemplate 类中封装了 DataSource 类,以 XML 配置形式注解关系即可 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
代码引用:
1
2
3
4
5
6"jdbcTemplate") (name =
private JdbcTemplate jdbcTemplate;
String sql = "SELECT * FROM t_user";
List<User> list = jdbcTemplate.query(sql, new MyRowMapper());
}
Spring 事务管理
编程式事务管理
此部分省略。
声明式事务管理
创建 Service 类和 Dao 类,再添加注入关系:
1) Service 层,又称业务逻辑层;
2) Dao 层,数据持久层,单纯对数据库进行操作.引入问题:ServiceTest 类中制造的异常,即造成了转账的不一致问题,细节如下:
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// ServiceTest.java
public class ServiceTest {
private DaoTest daotest;
public void setDaotest(DaoTest daotest) {
this.daotest = daotest;
}
/* @Transactional 注解方式时填写 */
public void executeUpdateInDB() {
GrowUp();
int exception = 10 / 0; // 人为制造异常
Dealth();
}
public void GrowUp() {
daotest.updateOneTuple("UPDATE t_user SET age=age+1 where username = ?",
new Object[]{"诸葛亮"}, new int[]{Types.VARCHAR});
}
public void Dealth(){
daotest.updateOneTuple("UPDATE t_user SET age=0 where username = ?",
new Object[]{"诸葛亮"}, new int[]{Types.VARCHAR});
}
}
// DaoTest.java
public class DaoTest {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* @param sql 插入元组的 SQL 语句
* @param obj 插入的属性值,与 SQL 中填写属性名的顺序相同
* @param types 对应属性值的数据类型
*/
public void updateOneTuple(String sql, Object[] obj, int[] types) {
jdbcTemplate.update(sql, obj, types);
}
}基于 XML 配置文件形式实现 ( AOP 的思想 )
目前为止,Spring 配置文件中约束添加完毕,以后配置复制此段即可。
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<!-- 目前为止,Spring 配置文件中约束添加完毕,以后的程序复制此段即可 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- set 方法注入属性 -->
<bean id="service" class="cn.kofes.service.ServiceTest">
<property name="daotest" ref="dao" />
</bean>
<bean id="dao" class="cn.kofes.dao.DaoTest">
<!-- 引用上例的 jdbcTemplate bean 即可 -->
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入 dataSource -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务增强 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
<!-- 做事务操作 -->
<tx:attributes>
<!--
| 事务操作方法的匹配规则,若事务操作的方法有规范命名,可以简写为:
| <tx:method name="execute*" />
-->
<tx:method name="executeTrading()" />
</tx:attributes>
</tx:advice>
<!-- 配置 AOP 操作 -->
<aop:config>
<!-- 切点 -->
<aop:pointcut id="poitncutB" expression="execution( * cn.kofes.service.ServiceTest.executeTrading(..) )" />
<!-- 切面 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcutB" />
</aop:config>
</beans>基于注解形式实现
在需要事务操作的类上配置注解
@Transactional
,再配置 applicationContext.xml:1
2
3
4
5
6
7
8
9<!-- 在 applicationContext.xml 中配置事务管理器 -->
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入 dataSource -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 开启注解事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
Hibernate
数据持久层
,Hibernate 是一个开放源码的 ORM
持久层框架。简单的说,Hibernate 只是一个将持久化类与数据库表相映射的工具,每个持久化类实例均对应于数据库表中的一个数据行而已
。用户只需直接使用面向对象的方法操作此持久化类实例,即可完成对数据库表数据的插入、删除、修改、读取等操作。
Hibernate 配置
使用配置文件将映射关系对应起来;
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<!-- 配置文件命名规范:*.hbm.xml -->
<hibernate-mapping>
<!--
| 配置类和表相对应:Class 标签
| name 属性:实体类全路径
| table 属性:数据库表名称
-->
<class name="cn.entity.User" table="t_user">
<!--
| 配置实体类 ID 与表 ID 对应:ID 标签
| Hibernate 要求实体类有一个属性唯一值,且要求表有字段作为唯一值
| name 属性:实体类的 id 属性名称
| column 属性:生产的表字段名称
-->
<id name="uid" column="uid">
<!-- 设置数据库表 id 增长策略:Class 标签
| 属性有:increment、hilo、squence、identity、native、uuid、guid 等。
-->
<generator class="native" />
</id>
<!-- 配置其他属性和表字段对应:Property 标签 -->
<!-- 若使用的是 SQL Server 数据库系统,Property 映射对的顺序要和数据库中列 (属性) 的顺序相对应 -->
<property name="username" column="username" type="string" />
<property name="age" column="age" type="int" />
</class>
</hibernate-mapping>
关于映射配置的一些批注:
- Note.01:就映射配置来说,这里千万要记住,若访问的是 SQL Server 数据库系统,则
映射对的顺序要和数据库中列 (属性) 的顺序相对应 。( 其他数据库系统没有出现此类问题,鉴于阅读与编程的规范,则不管使用哪种数据库系统驱动,都以此种方式编辑 Property 映射对 ) - Note.02:关于配置中实体类的主键生成策略有多种形式,详细见参考 [4]。
Note.03:User 实体类对象的状态:
瞬时态
:对象没有 ID 值,且其与 Session 没有关联。持久态
:对象有 ID 值,且其与 Session 有关联。托管态
:对象有 ID 值,但其与 Session 没有关联。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
* 下述代码只列举了细节不同的部分,其他细节见源代码
*/
User user = new User();
// 瞬时态:即插入元组
user.setUserName("Sample");
user.setAge(25);
session.saveOrUpdate(user);
// 托管态:对数据表中具体 ID 的元组进行数据修改,即更新元祖
user.setUid(attr_id);
user.setUserName("Sample");
user.setAge(25);
session.saveOrUpdate(user);
// session.save(user); // 注意:此操作为新增元组,但不是全部属性都修改,会引入空值
// 持久态:对数据表中具体 ID 的元组进行数据修改,即更新元祖
user = session.get(User.class, attr_id);
user.setUserName("Sample");
user.setAge(25);
session.saveOrUpdate(user);
// 启示:从持久态和托管态态的实验结果可知,修改元组,先查再改,防止空值。
创建 Hibernate 的核心配置文件;
核心配置文件格式是 XML,且核心配置文件名称和位置是
固定的
( Src 根目录下,且名称为hibernate.cfg.xml
)。Hibernate 操作过程中,仅加载核心配置文件。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<!-- 配置文件命名规范:hibernate.cfg.xml -->
<hibernate-configuration>
<session-factory>
<!-- (必填) 配置数据库信息:从 hibernate.property 中获取 -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<!-- SQL Server 数据库管理系统的 Driver
<property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.url">
jdbc:sqlserver://192.168.0.133:1433;DatabaseName=testDB
</property>
-->
<!-- MySQL 数据库管理系统的 Driver -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">
jdbc:mysql://192.168.0.163:3306/testDB?serverTimezone=UTC
</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password">pztech753</property>
<!-- (可选) 配置 Hibernate 信息:从 hibernate.property 中获取 -->
<!-- 操作数据库时,向控制台输出 SQL 语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 操作数据库时,向控制台输出格式化的 SQL 语句 -->
<property name="hibernate.format_sql">true</property>
<!-- Hibernate 配置自动建表:Update,有表更新没表建立 -->
<property name ="hibernate.hbm2ddl.auto">update</property>
<!--
| 例如,实现分页功能:
| MySQL 里面使用 LIMIT 关键字,Oracle 中使用 ROWNUM 关键字
| 让 Hibernate 识别不同数据库中特有的语句
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- (必填) 把映射文件放到核心配置文件中 ( 填 Src 之后的路径 ) -->
<mapping resource="cn/entity/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
关于核心配置的一些批注:
面对不同的数据库管理系统,对应的配置属性也存在差异,详细见参考 [5]。
通过 Session 保存实体类数据到数据库表中;
简单演示,详细见下一章节:Hibernate 使用
1
2
3
4User user = new User();
user.setUsername("Lucy");
user.setAge(25);
Session.save(user);
Hibernate 使用
以下代码为 Hibernate 最基本的使用方法,后续将通过优化 Configuration,以及规范事务来优化 Hibernate。
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// Step.01:加载 Hibernate 核心配置文件 hibernate.cfg.xml
Configuration cfg = new Configuration();
cfg.configure();
// Step.02:创建 SessionFactory 对象;
// 在此过程中,根据映射关系在数据库中把表创建起来
SessionFactory sessionFactory = cfg.buildSessionFactory();
// Step.03:使用 SessionFactory 创建 Session 对象;
Session session = sessionFactory.openSession();
// Step.04:手动开始事务;
Transaction trans = session.beginTransaction();
// Step.05:写具体逻辑,例如:增删改查;
// 例如添加操作 ( 不操作数据表,而操作实体类 )
User user = new User();
user.setUsername("Lucy");
user.setAge(25);
session.save(user);
// Step.06:提交事务;
trans.commit();
// Step.07:关闭资源;
session.close();
sessionFactory.close();Configuration
- 到 Src 目录下加载核心配置文件
hibernate.cfg.xml
;
- 到 Src 目录下加载核心配置文件
SessionFactory
- 根据核心配置文件中数据库配置、映射配置,且根据映射关系,到数据库中把表创建起来。
创建 SessionFactory 过程中,特别消耗资源,故应该采取优化措施:
在 Hibernate 操作中,建议一个项目创建一个 SessionFacotry 对象,以静态的工具类形式封装使用。
1
2
3
4
5
6
7
8
9
10
11
12
13public class HibernateUtils {
private static Configuration cfg = null;
private static SessionFactory sessionFactory = null;
static {
cfg = new Configuration();
cfg.configure();
sessionFactory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Session
- 类似 Jdbc 中的 Connection,可调用 Session 中的不同方法实现
增、删、改、查
操作。- 增加:save();
- 修改:update();
- 删除:delete();
- 查询:get();
Session 为单线程对象,即不能共用,仅自己使用。
Hibernate 已实现本地线程与 Session 的绑定:在 Hibernate 的核心配置文件中,再调用 sessionFactory 的方法得到。
1
<property name="hibernate.current_session_context_class">thread</property>
在 HibernateUtils 静态工具类中追加静态方法:
1
2
3
4
5
6
7
8
9public class HibernateUtils {
// 返回与本地线程绑定的 Session
public static Session getSessionObject() {
return sessionFactory.getCurrentSession();
}
}
/* 在其他类中调用该方法 */
Session session = HibernateUtils.getSessionObject();
- 类似 Jdbc 中的 Connection,可调用 Session 中的不同方法实现
Transaction
- 事务概念;
事务特性:原子性、一致性、隔离性、持久性;
隔离性:不考虑隔离性会产生的问题,如脏读、不可重复读和虚读。当然,可以设置隔离的级别来解决问题。
Hibernate 使用 Transaction 创建事务对象;
事务的 commit() 与 rollback() 方法 (
规范用法
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23SessionFactory sessionFactory = null;
Session session = null;
Transaction trans = nu
try {
sessionFactory = HibernateUtils.getSessionFactory()
session = sessionFactory.openSession();
// 开启事务
trans = session.beginTransaction();
// 人为制造异常,被除数不能为零
User user = ne0w User();
user.setAge(50/0);
session.save(user);
// 提交事务
trans.commit();
} catch(Exception ex) {
// 发生异常
trans.rollback();
} finally {
session.close();
sessionFactory.close();
}
Hibernate 优化
Hibernate 缓存机制
- 一级缓存:默认是打开的,其使用的范围为 Session 创建到关闭的范围,且存储数据必须为持久态数据。
一级缓存的特性:持久态的数据会自动更新数据库 ( 不用 session.update(user)、session.save(user) ),过程细节如下:
1
2
3
4
5
6
7
8// Get() 操作后,将返回持久态对象 user 存于一级缓存中,
// 及存一份到缓存中的快照区 (副本)。
user = session.get(User.class, attr_id);
// setXXX() 操作,同时修改持久态对象的值和一级缓存中的内容。
user.setUserName("Sample");
// 当提交事务时,会比较一级缓存和快照区,
// 若不相同,会触发更新数据库操作。
trans.commit();
- 二级缓存 (替代技术:
redis
):默认关闭,SessionFactroy 的使用范围。
Hibernate 查询
在
Hibernate 查询方式
小节将详细阐述。
Query 对象
推荐使用
使用 Query 对象,不需要写 SQL 语句,以 HQL 替代。
HQL:Hibernate Query Language,有别于 SQL 语句,即:SQL 操作表和表字段,而 HQL 操作实体类和属性。
Query 对象的使用:
1
2
3
4// 创建 Query 对象
Query query = session.createQuery("from User");
// 调用 query 对象的方法得到结果
List<User> list = query.list();
Criteria 对象
Ceiteria 对象的使用:
1
2
3
4// 创建 Criteria 对象
Criteria criteria = session.createCriteria(User.class);
// 调用 criteria 对象的方法得到结果
List<User> list = criteria.list();
SQLQuery 对象
SQLQuery 对象的使用:
1
2
3
4
5
6
7
8
9
10
11// 创建 SQLQuery 对象
String sql = "SELECT * FROM t_user t WHERE t.username= ? AND t.age = ?";
SQLQuery sqlQuery = session.createSQLQuery(sql)
.setParameter(0, "ABC").setParameter(1, 15);
// Case.01. 以数组形式接收结果
// List<Object []> list = sqlQuery.list();
// Case.02. 返回目标的 User 对象
sqlQuery.addEntity(User.class);
List<User> list = sqlQuery.list();
Hibernate 多表操作
- 数据库的多表查询
- 外键
- 指定外键关键字: Foreign Key(列名)
- 引用外键关键字: References <主表名>(主表主键)
- 关系
- 一对一
- 一对多
- 多对多
- 外键
一对多操作
一对多映射配置:
实体类配置
- 创建两个实体类,例如:公司和客户 ( 公司是一,客户是多 );
让两个实体类间互相表示;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 客户实体类
public class Customer {
// 省略其他属性
// ...
// 在客户实体类中表示所属公司,一个客户只属于一个公司
private Firm firm;
public void setFirm(Firm firm) { this.frim = firm; }
public Firm getFirm() { return firm; }
}
// 公司实体类
public class Firm {
// 省略其他属性
// ...
// Set 集合:无序,元素不可重复
private Set<Customer> customerSet = new HashSet<Customer>();
public Set<Customer> getCustomerSet() { return customerSet; }
public void setCustomerSet(Set<Customer> customerSet) { this.customerSet = customerSet; }
}
一对多映射配置:
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
26
27
28<!-- firm.bhm.xml -->
<class name="cn.kofes.entity.Firm" table="t_firm">
<!--
| 表示公司的所有客户
| Name 属性:属性值写公司实体类里表示客户 Set 集合的对象名称
-->
<set name="customerSet">
<!--
| 一对多关系建表,有外键
| Hibernate 机制,双向维护外键,即在一和多方都配置外键
| Column 属性:属性值为外键的名称,名称可自定义
-->
<key column="f_fid" />
<!-- 公司的所有客户,即 Class 里写客户实体类全路径 -->
<one-to-many class="cn.entity.Customer" />
</set>
</class>
<!-- customer.bhm.xml -->
<class name="cn.kofes.entity.Customer" table="t_customer">
<!--
| 表示客户所属公司
| Name 属性:客户实体类中使用 firm 对象表示
| Class 属性:Firm 实体类的全路径
| Column 属性:外键名称,名称可自定义
-->
<many-to-one name="firm" class="cn.entity.Firm" column="c_fid" />
</class>
核心配置文件中,加入 customer.bhm.xml 和 firm.bhm.xml。
1
2
3<!-- (必填) 把映射文件放到核心配置文件中 ( 填 Src 之后的路径 ) -->
<mapping resource="cn/entity/firm.hbm.xml" />
<mapping resource="cn/entity/customer.hbm.xml" />
一对多级联的操作实现
在原生数据库中,对包含外键的元组直接删除,是无法成功执行的。正确操作方式为:先删除外键约束,再删除对应元组。但在 Hibernate 中,不用考虑此类问题,因为内置封装了相关功能组件。
一对多级联的保存
1
2
3
4
5
6
7
8
9
10
11
12
13
14// Step.01.建立公司对象和客户对象的关系
Firm firm = new Firm();
Customer customer = new Customer();
// Step.02.把客户对象放到公司对象的 Set 集合里
firm.getCustomerSet().add(customer);
// Step.03.把公司对象放到客户对象里
customer.setFirm(firm);
/**
* 级联保存:即一方和多方都需配置数据
*/
session.save(customer);
session.save(firm);一对多级联的删除:在一方 ( 与多方对立 ) 即公司对象,修改其配置文件。
最后,直接在代码中进行删除操作即可。
1
2
3
4
5
6
7<!-- firm.bhm.xml -->
<class name="cn.kofes.entity.Firm" table="t_firm">
<!-- 对 Set 标签的 casade 值为 delete -->
<set name="customerSet" cascade="delete">
...
</set>
</class>
优化步骤之
Inverse 属性
因为 Hibernate 是双向维护外键,在公司和客户里面都需维护外键 ( 从级联删除的执行过程中可体验出来 )。故我们可以设置其中的一方
放弃
维护外键,以优化性能。1
2
3
4
5
6
7
8
9
10
11<!-- firm.bhm.xml -->
<class name="cn.kofes.entity.Firm" table="t_firm">
<!--
| Inverse 属性
| Flase,表示不放弃关系维护
| True,表示放弃关系维护
-->
<set name="customerSet" cascade="save-update, delete" inverse="true">
...
</set>
</class>
多对多操作
多对多映射配置:实体类配置
- 创建两个实体类,例如:用户和角色 ( 一个用户可扮演多个角色,一个角色可由多个用户扮演 );
让两个实体类间互相表示;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 用户实体类
public void User {
// 省略其他属性
// ...
private Set<Role> roleSet = new HashSet<Role>();
public Set<Role> getRoleSet() { return roleSet; }
public void setRoleSet(Set{Role} roleSet) { this.roleSet = roleSet; }
}
// 角色实体类
public void Role {
// 省略其他属性
// ...
private Set<User> userSet = new HashSet<User>();
public Set<User> getUserSet() { return userSet; }
public void setUserSet(Set<User> userSet { this.userSet = userSet; }
}
多对多映射配置:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41<!-- user.bhm.xml -->
<class name="cn.kofes.entity.User" table="t_user">
<!--
| 在用户中,表示所有的角色
| Name 属性:属性值写用户实体类里表角色 Set 集合的对象名称
| Table 属性:填写多对多时,两实体的联系转化成表 ( 第三张表 )
-->
<set name="roleSet" table="t_user_role">
<!--
| 多对多关系建表,有外键
| Column 属性:当前用户实体,在第三张表中的外键名称
-->
<key column="u_fid" />
<!--
| Class 属性:填写角色实体类全路径
| Column 属性:角色实体,在第三张表中的外键名称
-->
<many-to-many class="cn.entity.Role" column="r_fid" />
</set>
</class>
<!-- role.bhm.xml -->
<class name="cn.kofes.entity.Role" table="t_role">
<!--
| 在角色中,表示所有的用户
| Name 属性:属性值写角色实体类里表示用户 Set 集合的对象名称
| Table 属性:填写多对多时,两实体的联系转化成表 ( 第三张表 )
-->
<set name="userSet" table="t_user_role">
<!--
| 多对多关系建表,有外键
| Column 属性:当前角色实体,在第三张表中外键的名称
-->
<key column="r_fid" />
<!--
| Class 属性:填写用户实体类全路径
| Column 属性:用户实体,在第三张表中外键的名称
-->
<many-to-many class="cn.entity.User" column="u_fid" />
</set>
</class>
核心配置文件中,加入 customer.bhm.xml 和 firm.bhm.xml。
1
2
3<!-- (必填) 把映射文件放到核心配置文件中 ( 填 Src 之后的路径 ) -->
<mapping resource="cn/entity/user.hbm.xml" />
<mapping resource="cn/entity/role.hbm.xml" />多对多级联的操作实现
多对多级联保存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 和一对多级联操作一样,在用户配置文件 user.hbm.xml 中,
// 对 Set 标签的 casade 值为 save-update
// 多对多级联保存
User user_1 = new User();
User user_2= new User();
Role role_1 = new Role();
Role role_2 = new Role();
Role role_3 = new Role();
// user_1 拥有角色 1、2,user_2 拥有角色 2、3
user_1.getRoleSet().add(role_1);
user_1.getRoleSet().add(role_2);
user_2.getRoleSet().add(role_2);
user_2.getRoleSet().add(role_3);
// 保存用户即可 ( 对应于配置文件中设置了 casade 值的实体 )
session.save(user_1);
session.save(user_2);多对多级联删除 ( 不推荐 ):配置文件;最后,直接在代码中进行删除操作即可。
1
2
3
4
5
6
7
8// 和一对多级联操作一样,在用户配置文件 user.hbm.xml 中,
// 对 Set 标签的 casade 值为 delete ( 可以与保存的属性共存 )
<!-- user.bhm.xml -->
<class name="cn.kofes.entity.User" table="t_user">
<set name="roleSet" table="t_user_role" casade="save-update, delete">
...
</set>
</class>维护多对多的关系,通过维护
第三张表
( 两实体的联系转化成的表 ) 实现。
Hibernate HQL 查询详解
OID
查询和对象导航
查询
1 | // 单元测试类 |
HQL 查询
1 | // Step.01.创建 Query 对象,写 HQL 语句实现查询 |
HQL 多表查询
内连接:连接两个表有关联的数据,忽略两个表中对应不起来的数据。
- SQL:SELECT * FROM t_user AS u, t_role AS r ON u.uid = r.uid;
- 或者:SELECT * FROM t_user u INNER JOIN t_role r ON u.uid = r.uid;
- SQL:SELECT * FROM t_user AS u, t_role AS r ON u.uid = r.uid;
左外链接:左边表所有元组,右边表关联数据。
- 若左表与右表没有关联数据,则右边表数据补 NULL 值,多了则删除
- SQL:SELECT * FROM t_user u LEFT OUTER JOIN t_role r ON u.uid = r.uid;
- 右外链接:与左外链接同理。
1 | // HQL 内连接 ( 左、右外链接同理 ) |
Hibernate 检索策略
立即查询
根据 ID 查询,调用 get() 方法,则马上发送语句查询数据库。
1 | // Debug 方式可检验,即执行代码马上发送 SQL 语句 |
延迟查询
根据 ID 查询,调用 load() 方法,不会马上发送语句查询数据库,只有得到对象的值时,才发送语句查询数据库。
1 | // 调用 load() 方法之后,并不会马上发送 SQL 语句 |
- 类级别延迟:例如,根据 ID 查询返回实体类对象,调用 load() 方法不会马上发送 SQL 语句。
关联级别查询:例如上述
对象导航查询
,查询某个公司后,再查询这间公司的所有客户,查询公司所有客户的过程是否需要延迟,则称其为关联级别延迟。1
2
3
4
5
6
7
8
9
10
11
12
13<!--
| 在配置文件中实现关联级别延迟
| fetch 属性:select
| lazy 属性:true / false / extra
| - True,当得到对象里其他值时,即非 ID 值,才发送 SQL 语句进行查询。
| - False,反之,会多执行一次查询 ( getUid() )。
| - Extra,需要什么属性,查询什么属性 ( 仅需少量属性时推荐使用 )
-->
<class name="cn.kofes.entity.User" table="t_user">
<set name="roleSet" table="t_user_role" fetch="select" lazy="true" >
...
</set>
</class>
批量抓取
例如,我们需要查询所有公司的所有客户,代码实现如下:
1
2
3
4
5
6
7
8
9
10
11Criteria criteria = session.createCriteria(User.class);
List<Firm> list = criteria.list();
for(Firm firm : list) {
System.out.println( firm.getId() + ":" + firm.getName() );
Set<Customer> customerSet = firm.getCustomerSet();
for(Customer customer : customerSet) {
System.out.println( customer.getUid() + ":" + customer.getUsername() );
}
}
// 但在执行过程中,暴露的问题时:每次循环都执行一次查询,拖沓性能
故我们只需要在实体映射配置文件中,对 Set 标签进行设置属性即可:
1
2
3
4
5
6
7<!-- firm.bhm.xml -->
<class name="cn.kofes.entity.Firm" table="t_firm">
<!-- batch-size 的值为整数即可 -->
<set name="customerSet" batch-size="10">
...
</set>
</class>
SSH 框架总结
Struts
- Action 操作
- Action 创建 (三种方式):继承
ActionSupport
类。 - Action 访问路径:创建
struts.xml
配置文件,文件名称和位置 ( Src 目录 ) 固定。 - 访问 Action 的多个方法:使用
通配符方式
配置。 - Action 获取表单提交数据:
- 获取 Request 对象 ( 使用
ServletAction
类 ); - 属性封装;
- 模型驱动;
- 获取 Request 对象 ( 使用
- Action 操作域对象:使用
ServletAction
类。 - 配置 Struts2 过滤器。
- Action 创建 (三种方式):继承
- 值栈
- 向值栈放数据:
- Set 方法;
- Push 方法;
- 定义变量,生成 Get 方法.
- 从值栈获取数据:在 JSP 中使用 Struts2 标签 + Ognl 获取
<s:property />
<s:iterator />
- 向值栈放数据:
- 拦截器
- AOP 和责任链模式;
- 自定义拦截器:
- 继承
MethodFilterInterceptor
类,并重写类方法; - 配置拦截器和 Action 关联.
- 继承
Spring
- Spring 核心配置文件:
- 名称和位置没有固定要求;
- 在 Spring 核心配置文件中引入 Schema 约束.
- 创建对象:
- XML 配置方式:
<bean id="" class="" />
; - 注解方式:
@Component
或@Controller
(Web层)、@Service
(业务层)、@Repository
(持久层).
- XML 配置方式:
- 注入属性:
- XML 配置方式:
<bean id="" class=""><property name="" ref="" /></bean>
; - 注解方式:
@autowired
、@Resource(name="value")
.
- XML 配置方式:
- 使用 ServletContext 对象和监听器实现
- 在服务器启动时,加载 Spring 配置文件,创建对象;
- 配置 Spring 的监听器;
- 指定 Spring 配置文件位置.
- Spring JdbcTemplate
- Spring 事务配置:
- XML 配置方式;
- 注解方式:
@Transational
Hibernate
- ORM 思想:
对象关系映射
,参考 ORM 框架; - 数据库信息配置:MySQL / SQL Server 2008 R2;
Hibernate 信息配置:即配置 Hibernate 核心配置文件 (
hibernate.cfg.xml
)。Hibernate 和 Spring 整合时,配置文件的名称和位置是没有固定要求的。
映射关系配置:即配置 Hibernate 映射配置文件 (
xxx.hbm.xml
),实体类和数据表映射关系 ( 使用 ORM 思想 )。- Hibernate 操作步骤:Spring 框架对 Hibernate 框架也有封装,即
HibernateTemplate
类。
SSH 框架整合
Spring 与 Struts 2 框架的整合
把 Struts 2 的 Action 对象创建交给 Spring 进行管理。
1
<bean id="" class="" scope="prototype" />
Spring 与 Hibernate 框架的整合
把 Hibernate 的核心配置文件里的数据库配置,直接写在 Spring 配置文件中。且把 SessionFactory 对象创建交给 Spring 管理。
SSH 整合演示
Spring 与 Struts 2 整合
Action 代码实现:
1
2
3
4
5
6
7
8
9// cn.kofes.dao.BaseAction.java
public class BaseAction extends ActionSupport {
public String execute() throws Exception {
System.out.println("The portion of action has deployed successfully.");
return NONE;
}
}
在 Spring 配置文件下整合 Struts:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<!-- spring.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 整合 Struts:Action 对象的配置 -->
<bean id="BaseAction" class="cn.kofes.action.BaseAction" scope="prototype"/>
</beans>整合 Web,当服务器启动时加载 Spring 配置即初始化,我们需要在 web.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
26
27
28
29
30
31
32
33
34<!-- web.xml -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 加载 Spring 的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!-- 监听器模块:作用就是启动 Web 容器时,自动装配 spring.xml 文件的配置信息 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 过滤器模块 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
最后,struts.xml 填入以下配置即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<struts>
<package name="sample" extends="struts-default" namespace="/">
<!--
| Class 属性值不写目标 Action 的全路径,原因是防止二次创建对象
| 引入 Spring 相对应 Action 的 Bean 标签 ID
-->
<action name="BaseAction" class="BaseAction" />
</package>
</struts>
Spring 与 Hibernate 整合
在 Spring 配置文件下配置数据库信息,及整合 Hibernate。后者即将 SessionFacotry 交由 Spring 管理:
c3p0.properties
和hibernate.properties
配置文件位于 Src 根目录下,键值分离以便以后修改配置。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<!-- spring.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 在根目录下新建文件 c3p0.properties,存储数据库连接信息 -->
<context:property-placeholder location="classpath:c3p0.properties"/>
<!-- Data Connection Settings -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 引用 c3p0.properties 的键值对即可,格式如 ${key.value} -->
<property name="driverClass" value="${datasource.driverClass}"/>
<property name="jdbcUrl" value="${datasource.jdbcUrl}"/>
<property name="user" value="${datasource.user}"/>
<property name="password" value="${datasource.password}"/>
<!-- 设置数据库连接池的最大连接数 -->
<property name="maxPoolSize" value="${datasource.maxPoolSize}"/>
<!-- 设置数据库连接池的最小连接数 -->
<property name="minPoolSize" value="${datasource.minPoolSize}"/>
<!-- 设置数据库连接池的初始化连接数 -->
<property name="initialPoolSize" value="${datasource.initialPoolSize}"/>
<!-- 设置数据库连接池的连接最大空闲时间 -->
<property name="maxIdleTime" value="${datasource.maxIdleTime}"/>
<!-- c3p0缓存Statement的数量数 -->
<property name="maxStatements" value="${datasource.maxStatements}"/>
<!-- 当连接池的连接用完的,从 C3p0 下获取新的连接数 -->
<property name="acquireIncrement" value="${datasource.acquireIncrement}"/>
<property name="checkoutTimeout" value="${datasource.checkoutTimeout}"/>
<property name="idleConnectionTestPeriod" value="${datasource.idleConnectionTestPeriod}"/>
</bean>
<!-- 整合 Hibernate:SessionFactory 对象的配置 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- (必填信息) 指定数据库 -->
<property name="dataSource" ref="dataSource" />
<!-- (可选信息) 配置 Hibernate 信息 -->
<property name="hibernateProperties" value="classpath:hibernate.properties" />
<!-- (必填信息) 把映射文件放到核心配置文件中 ( 现直接加载到 LocalSessionFactoryBean 类中即可 ) -->
<property name="mappingResources">
<list>
<value>mapper/sample.hbm.xml</value>
</list>
</property>
</bean>
</beans>创建实体类:
1
2// Employee.java
public class Sample { ... }
创建实体类映射文件 ( 文件位置没有固定要求,建议统一文件夹存储 ):
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<!-- src/resource/HbmCollection/sample.hbm.xml -->
<hibernate-mapping>
<!--
| name 属性:实体类全路径
| table 属性:数据库表名称
-->
<class name="cn.kofes.bean.Sample" table="t_sample">
<!--
| Hibernate 要求实体类有一个属性唯一值,且要求表有字段作为唯一值
| name 属性:实体类的 id 属性名称
| column 属性:数据表字段名称
-->
<id name="id" column="id">
<!-- 设置数据库表 id 增长策略:Class 标签
| 属性值:increment、hilo、squence、identity、native、uuid、guid 等。
-->
<generator class="native"/>
</id>
<!-- 配置其他属性和表字段对应:Property 标签 -->
<property name="name" column="name" type="string"/>
</class>
</hibernate-mapping>在
核心配置文件
中引入映射配置文件
:在 Spring 配置文件中已引入映射配置文件,故不需要再单独配置
hibernate.cfg.xml
。事务配置:在 spring.xml 配置文件中配置并开始事务注解,再到 Service 层添加注解
@Transactional
:1
2
3
4
5
6
7
8<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 开始事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager" />
Spring 分模块开发
- 在 Spring 里配置多个内容,容易造成配置混乱,不利于维护。
把 Spring 核心配置文件中,将一部分配置放到单独的配置文件中,再在 Spring 核心配置文件中引入单独配置文件。
1
2
3
4
5<!--
| 将原 Spring 中的部分配置放置到独立 xml 中,
| 例如我把 Dao 层的配置独立出来,再到 spring.xml 中引入配置文件 dao.xml 即可。
-->
<import resource="classpath:dao.xml" />
SSH 框架使用演示
SSH 整合工作告一段落,当然你可通过 Maven 打包工程,以便以后复用。
SSM 框架
当然,随着框架技术的迭代更新,更加科学、合理的新框架也逐渐在项目中使用起来,如 SSM 框架。具体细节可参考:Java EE 之 SSM 框架配置与使用
SSM 框架
:Spring MVC、Spring、MyBatisSSM ( Spring + SpringMVC + MyBatis ) 框架集由 Spring、SpringMVC、MyBatis 三个开源框架整合而成,常作为数据源较简单的 Web 项目的框架。
Spring
:是一个轻量级的控制反转 (IoC) 和面向切面 (AOP) 的容器框架。与本文的 Spring 章节内容相同。
SpringMVC
:分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。Mybatis
:Mybatis 是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs (Plain Old Java Objects,普通的 Java对象 ) 映射成数据库中的记录。
参考资料
- [1] 一枪尽骚 · 魂. 基于全注解方式的SSH基础框架. csdn.net
- [2] 刻下岁月. IntelliJ IDEA 2016.1.2 + Spring + Struts2 + Hibernate ( SSH ). lunhui.ren
- [3] kent. JavaEE SSH 三大框架整合 ( Spring + Struts2 + Hibernate ). cnblogs.com
- [4] Starskyhu. Hibernate 各种主键生成策略与配置详解. cnblogs.cn
- [5] 陈铁锋. Hibernate 连接三种数据库的配置 ( SQL Server、Oracle、MySQL ). csdn.net
- [6] RoadOfStudy. MySQL 5.7版本的root用户重置密码问题. cnbolgs.com
- [7] Time Tries All. MySQL 重置密码出现的一系列问题. csdn.net
- [8] Souvc. 连接虚拟机 (Ubuntu16.04) 的 MySQL 服务器. souvc.com