框架 | Java EE 之 SSH 框架配置与使用

序言

本文章主要围绕 J2EE 中 SSH ( Spring、Struts、Hibernate ) 框架的配置以及使用问题展开学习的,最终目的是输出可复用的版本,以供后续的项目复用。当然,学习和配置的过程难免有不恰当或错误之处,还望朋友指出、斧正。

教学资源

更新进度

  • 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 下配置目录访问路径即可 ( 一般规范填写项目名 )。

      Tomcat 9.0.x Deployment

      图 4-1 Tomcat 9.0.x Deployment
    • 紧接着,在菜单栏 File > Project Structure > Artifacts 下配置,把对应的 module 的 Available Elements Put into Output Rootxxx:war_exploded 下。

      Project Structure Artifacts

      图 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 构建框架。

版本信息

  • 基于以下版本,打包的 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 框架:SpringStruts2Hibernate

集成 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 解决的问题:

    Struts2解决的问题

    图 6-1 Struts2 解决的问题
Struts2 案例
  • 创建 Action:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // BaseAction.java

    public class BaseAction extends ActionSupport {
    @Override
    // 每次访问 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
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

    <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
    <?xml version="1.0" encoding="UTF-8"?>
    <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 底层执行过程图示。

Struts底层执行过程

图 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 版本的约束 -->

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">
    <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
    6
    public class BaseAction implements Action {
    @Override
    public String execute throws Exception {
    return NONE; // SUCCESS、ERROR ( 也可自定义字符串 ) ...
    }
    }
  • 创建类,继承 ActionSupport;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class BaseAction extends ActionSupport {
    @Override
    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
    37
    public 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;

    @Overrride
    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
    20
    public 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();
    @Override
    public User getModel() {
    return user;
    }
    @Override
    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; }

    @Override
    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 -->
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
    "http://struts.apache.org/dtds/struts-2.5.dtd">

    <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 层:界面交互层
    @Controller(value="formOperator")
    @Scope(value = "prototype")
    public class FormOperator extends ActionSupport {
    private User user;

    // Service 处理业务逻辑
    @Resource(name = "sampleService")
    private BaseService service;

    @Override
    public Sring execute() throw EXception {
    return NONE;
    }

    public String saveInfo() {
    service.saveUserInfo(user);
    return "info";
    }

    public String getInfo() {
    // 从数据库中加载数据
    return NONE;
    }
    }

    // SampleServiceImpl.java
    // Servie 层:业务逻辑层
    @Service(value = "sampleService")
    @Transactional
    public class SampleServiceImpl implements BaseService {

    @Resource(name = "sampleDao")
    private BaseDao dao;

    @Override
    public void saveUserInfo(User user) {
    if( null != dao.queryOneTuple(user.getClass(), user.getUid()) ) {
    dao.updateOneTuple(user);
    } else {
    dao.insertOneTuple(user);
    }
    }
    }

    // SampleDaoImpl.java
    // Dao 层:数据持久层
    @Repository(value = "sampleDao")
    public class SampleDaoImpl implements BaseDao {

    @Resource(name = "hibernateTemplate")
    private HibernateTemplate hibernateTemplate;

    @Override
    public void insertOneTuple(Object entity) {
    hibernateTemplate.save(entity);
    }

    @Override
    public void updateOneTuple(Object entity) {
    hibernateTemplate.update(entity);
    }

    @Override
    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 页面中获取到值栈数据。
  • Ognl:Web 阶段,EL 表达在 Jsp 中获取域对象中的值。而 Ognl 也是一种表达式。

    • Struts 中操作值栈数据,和 Struts 标签 一起使用、操作值栈。
    • Ognl 不是 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;

    @Override
    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 { ... }

    Context 存储的对象引用

    图 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 MVC
    • Service 层:ioC
    • DAO 层: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 -->

    <?xml version="1.0" encoding="UTF-8"?>
    <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() {
    @Test
    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
    3
    public class UserOperator {
    public UserOperator() { }
    }
  • 使用静态工厂创建对象

    1
    2
    3
    4
    5
    6
    7
    public 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
    6
    public class UserOperator {
    private String operationType;
    public void setUsername(String operationType) {
    this.operationType = operationType;
    }
    }
  • 有参数构造注入

    1
    2
    3
    4
    5
    6
    public 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
    15
    public class UserOperator {

    private User user;

    public void setUser(User user) {
    this.user = user;
    }

    @Test
    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
      17
      public class UserOperatorXML {
      private String arrs; // 数组
      private List<String> list; // List 集合
      private Map<String, String> map; // Map 集合
      private Properties properties; // Properties 类型

      // Setter() 方法此处省略...

      @Test
      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
    <!-- -- 有参数构造注入属性 -- -->
    <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">
    <lsit><value>Arrs0</value></lsit>
    <lsit><value>Arrs1</value></lsit>
    <lsit><value>Arrs2</value></lsit>
    </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
    @Component("BaseAction")
    @Scope("prototype")
    public class BaseAction { ... }
基本内容
  • 使用注解创建对象
  • 使用注解注入对象
  • xml 和注解方式混合使用
小试牛刀
  • 创建类和方法

    1
    2
    3
    public 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 -->

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

    @Component(value="user")
    public class User {
    // 省略实体类的属性...
    }

    public class UserOperatorAnn {
    private User user;

    @Test
    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")

    @Autowired
    private User user;

    @Test
    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 {
      // 注解方式注入属性
      @Resource(name = "user")
      private User user;
      @Resource(name = "customer")
      private Customer customer;
      @Resource(name = "firm")
      private Firm firm;

      @Test
      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
      9
      public interface Dao {
      public void add();
      }

      public class DaoImpl implements Dao {
      public void add() {
      // 具体逻辑
      }
      }
AOP 相关术语

便于理解,引入实体类进行说明:

1
2
3
4
5
6
public class User {
public void add() {}
public void update() {}
public void delete() {}
public void findAll() {}
}
  • 连接点 (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; }

    @Test
    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 -->

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
    @Aspect
    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
    */
    @Around(value="execution( * cn.kofes.entity.UserOperatorXML.*(..) )")
    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> {
    @Override
    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
    @Resource(name = "jdbcTemplate")
    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 配置文件中约束添加完毕,以后的程序复制此段即可 -->
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-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 -->

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <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 -->

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

    <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
    4
    User 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
  • SessionFactory

    • 根据核心配置文件中数据库配置、映射配置,且根据映射关系,到数据库中把表创建起来。
    • 创建 SessionFactory 过程中,特别消耗资源,故应该采取优化措施:

      在 Hibernate 操作中,建议一个项目创建一个 SessionFacotry 对象,以静态的工具类形式封装使用。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public 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
        9
        public class HibernateUtils {
        // 返回与本地线程绑定的 Session
        public static Session getSessionObject() {
        return sessionFactory.getCurrentSession();
        }
        }

        /* 在其他类中调用该方法 */
        Session session = HibernateUtils.getSessionObject();
  • 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
      23
      SessionFactory 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
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
// 单元测试类
public class HibernateTest {
@Test
public void testSelect1() {
SessionFactory sessionFactory = null;
Session session = null;
Transaction trans = null;

try {
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
trans = session.beginTransaction();

// 需求:根据 ID 查询某个公司,再查询这个公司的所有客户

// OID 查询:查询 ID 为 1 的公司
Firm firm = session.get(Firm.class, 1); //
// 对象导航查询:根据某条记录的 ID,返回对象 (公司的客户)
Set<Customer> customer = firm.getCustomerSet();
System.out.print( customer.toString() );

trans.commit();
} catch(Exception ex) {
e.printStackTrace();
transaction.rollback();
} finally {
session.close();
sessionFactory.close();
}
}
}
HQL 查询
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
// Step.01.创建 Query 对象,写 HQL 语句实现查询
// Step.02.调用 query 对象的方法得到结果

// 查询所有
Query query = session.createQuery("FROM t_user");
List<User> list = query.list();

// 条件查询
String sql_obscure = "FROM t_user WHERE username LIKE ?"; // 模糊查询
String sql_accurate = "FROM t_user WHERE age = ? AND username = ?"; // 精确查询
Query query = session.createQuery(sql_accurate);
// arg0 为占位符位置,arg1 为参数
query.setParameter(0, 25).setParameter(1, "Lucy");
List<User> list = query.list();

// 排序查询:ASC,升序 / DESC,降序
String sql = "FROM t_user ORDER BY uid ASC";
Query query = session.createQuery(sql);
List<User> list = query.list();

// 分页查询:LIMIT 关键字
String sql = "FROM t_user";
Query query = session.createQuery(sql);
query.setFirstResult(0);
query,.setMaxResults(10);
List<User> list = query.list();

// 投影查询
String sql = "SELECT username, age FROM t_user";
Query query = session.createQuery(sql);
List<Object> list = query.list();

// 聚集函数
// COUNT()、SUM()、MAX()、MIN()...
String sql = "SELECT COUNT(*) FROM t_user";
Query query = session.createQuery(sql);
// 将结果以对象形式返回
Object obj = query.uniqueResult();
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;
  • 左外链接:左边表所有元组,右边表关联数据。

    • 若左表与右表没有关联数据,则右边表数据补 NULL 值,多了则删除
    • SQL:SELECT * FROM t_user u LEFT OUTER JOIN t_role r ON u.uid = r.uid;
  • 右外链接:与左外链接同理。
1
2
3
4
5
6
7
8
9
10
11
12
// HQL 内连接 ( 左、右外链接同理 )
String sql_inner_join = "FROM t_user u INNER JOIN u.roleSet";
Query query = session.createQuery(sql_inner_join);
List list = query.list(); // list 返回的每部分都是数组

// HQL 迫切内连接
String sql_inner_join_fetch = "FROM t_user u INNER JOIN FETCH u.roleSet";
Query query = session.createQuery(sql_inner_join_fetch);
List list = query.list(); // list 返回的每部分都是对象

// HQL 迫切左外连接 ( 没有迫切右外连接 )
String sql_inner_join_fetch = "FROM t_user u LEFT OUTER JOIN FETCH u.roleSet";
Hibernate 检索策略
立即查询

根据 ID 查询,调用 get() 方法,则马上发送语句查询数据库。

1
2
// Debug 方式可检验,即执行代码马上发送 SQL 语句
User user = session.get(User.class, 1);
延迟查询

根据 ID 查询,调用 load() 方法,不会马上发送语句查询数据库,只有得到对象的值时,才发送语句查询数据库。

1
2
3
4
5
// 调用 load() 方法之后,并不会马上发送 SQL 语句
User user = session.load(User.class, 1);
System.out.println( user.getUid() );
// 当得到对象里其他值时,即非 ID 值,才发送 SQL 语句进行查询
System.out.println( user.getUsername() );
  • 类级别延迟:例如,根据 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
    11
    Criteria 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 类 );
      • 属性封装;
      • 模型驱动;
    • Action 操作域对象:使用 ServletAction 类。
    • 配置 Struts2 过滤器。
  • 值栈
    • 向值栈放数据:
      • 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 配置方式:<bean id="" class=""><property name="" ref="" /></bean>
    • 注解方式:@autowired@Resource(name="value").
  • 使用 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 框架整合

SSH框架整合思想

图 6-4 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 {
    @Override
    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 -->

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-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 -->

    <?xml version="1.0" encoding="UTF-8"?>
    <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
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

    <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.propertieshibernate.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 -->

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-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 -->

    <?xml version='1.0' encoding='UTF-8'?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

    <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、MyBatis

    SSM ( Spring + SpringMVC + MyBatis ) 框架集由 Spring、SpringMVC、MyBatis 三个开源框架整合而成,常作为数据源较简单的 Web 项目的框架。

  • Spring:是一个轻量级的控制反转 (IoC) 和面向切面 (AOP) 的容器框架。

    与本文的 Spring 章节内容相同。

  • SpringMVC:分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。

  • MybatisMybatis 是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs (Plain Old Java Objects,普通的 Java对象 ) 映射成数据库中的记录。

参考资料