【系列】使用springmvc+mybatis创建Web应用(二)—— 数据库、配置和测试

上一篇文章简单地介绍了如何从零开始构建一个Spring MVC应用,如果我们仅仅需要做一些静态页面或者数据不变化的Web应用,那么其实这样就足够了。这当然是不现实的,我们的页面中的数据需要不断地变化,不同的用户登录进来之后应用只能看到属于自己的数据,诸如此类的需求告诉我们原本的代码框架是不够的。

对于一个功能相对齐整的Web应用,除了上一篇文章中介绍的代码框架,还需要:

  1. 连接和操作数据库
  2. 配置文件
  3. 单元测试(虽然可以省略,但还是建议保留)

接下来的内容就介绍上面所列举的内容,以期能创建一个比较完整的Web应用框架。

使用Mybatis操作数据库

上一篇文章中已经简单地介绍过了Mybatis中的基本概念,下面直接进入实战环节。由于要依赖第三方的工具包,pom.xml文件中需要新添加依赖如下:

<!-- database -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.1</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.1.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.26</version>
</dependency>
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

<!-- test -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
</dependency>

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>0.8.1</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path-assert</artifactId>
    <version>0.8.1</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.9.5</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- test end -->

JDBC连接池

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。

在项目中使用连接池是非常有必要,主要有以下好处:

  • 减少连接创建时间
  • 简化的编程模式
  • 受控的资源使用

Java中有以下常用的开源连接池:

  • C3P0
  • DBCP
  • Proxool

在本文中将使用C3P0来作为数据库连接池组件,并且用Spring来管理C3P0。为了使用Spring来管理应用中的所有组件,我们先在Web.xml文件中配置下面的代码,使得Spring的上下文(context)文件能够随着应用一块启动。

<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的上下文配置的路径为:classpath:applicationContext.xml。在笔者刚开始学习Java的很长一段时间里,都不太明白什么是classpath,以致于常常取不到资源文件。

classpath表示一个绝对路径,但不是一个固定值。它是编译完毕后存放xxx.class文件的最顶层目录所对应的路径。譬如使用Maven编译之后通常(如果没有手动修改Maven配置)会将编译后的class文件放置在target\classes目录下,于是classpath表示的绝对路径为:工程根路径\target\classes\。所以,本文中的Spring的上下文配置文件的绝对路径为:工程根路径\target\classes\applicationContext.xml

src/main/resources资源文件夹下创建一个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:tx="http://www.springframework.org/schema/tx" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <!-- 配置数据连接池(C3P0) -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8" />
        <property name="user" value="test" /> <!-- 数据库用户名 -->
        <property name="password" value="test" /> <!-- 数据库密码 -->
        <property name="maxPoolSize" value="10" />
        <property name="minPoolSize" value="1" />
        <property name="initialPoolSize" value="2" />
        <property name="maxIdleTime" value="20" />
        <property name="acquireIncrement" value="3" />
        <property name="idleConnectionTestPeriod" value="60" />
        <property name="unreturnedConnectionTimeout" value="190" />
        <property name="checkoutTimeout" value="20000" />
        <property name="acquireRetryAttempts" value="30" />
    </bean>

    <!-- 配置数据库事务 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

数据库表

Mybatis的主要功能是将Mapper配置映射成对应的sql语句并执行。在介绍Mybatis的配置之前,先创建一个简单的数据表作为数据示例。

数据表User的结构如下:

字段类型索引字段含义
usernamevarchar(50)主键用户名
passwordvarchar(50)密码
roleint用户角色

笔者使用的是mysql数据库,sql语句如下:

CREATE DATABASE test; 

USE test;

CREATE TABLE `test`.`User`(  
  `username` VARCHAR(50) COMMENT '用户名',
  `password` VARCHAR(50) COMMENT '密码',
  `role` INT COMMENT '角色'
) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci;

INSERT INTO User VALUES("test", "test", 1); # 插入一条记录

Mybatis配置

src/main/resources资源文件夹中创建一个spring-mybatis.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.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- mybatis SqlSessionFactory配置 -->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:/mapper/*.xml" />
    </bean>

    <!-- Mapper 扫描配置 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sessionFactory" />
        <property name="basePackage" value="demo.dao" />
    </bean>

</beans>

在【mybatis SqlSessionFactory配置】配置项中,指定了mybatis的Mapper文件所在的位置(classpath:/mapper/*.xml)。于是,在src/main/resources资源文件夹创建一个mapper子目录,将所有的Mapper文件放置在这个文件夹下。

applicationContext.xml文件中引入spring-mybatis.xml,使其加入spring的管理容器中。

<import resource="classpath:spring-mybatis.xml"/>

Dao接口和Mapper配置

首先创建一个demo.model包,在这个包下创建一个名为User的类。这个类的字段与上面设计的数据表里面的字段是一一对应的。

package demo.model;

/**
 * 数据模型,与数据库表里面的字段对应。
 * @author xialei
 *
 */
public class User {

    private String username;

    private String password;

    private Integer role;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getRole() {
        return role;
    }

    public void setRole(Integer role) {
        this.role = role;
    }
}

接着创建一个demo.dao包,在这个包下创建一个名为UserMapper的类。这个类的作用是定义一系列数据操作方法。

package demo.dao;

import org.apache.ibatis.annotations.Param;

import demo.model.User;

public interface UserMapper {

    /**
     * 访问数据库,检查用户名和密码输入是否正确。
     * @param username
     * @param password
     * @return
     */
    public User check(@Param("username") String username, @Param("password") String password);
}

最后创建UserMapper所对应的Mapper文件(UserMapper.xml),mybatis会根据这个文件生成这个接口的一个实现类。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<!-- demo.dao.UserMapper与UserMapper定义路径一致 -->
<mapper namespace="demo.dao.UserMapper" >

  <!-- demo.model.User与User的定义路径一致,下面是Java对象与数据库字段的映射关系。 -->
  <resultMap id="BaseResultMap" type="demo.model.User" >
    <id column="username" property="username" jdbcType="VARCHAR" />
    <result column="password" property="password" jdbcType="VARCHAR" />
    <result column="role" property="role" jdbcType="TINYINT" />
  </resultMap>
  <sql id="Base_Column_List" >
    username, password, role
  </sql>

  <!-- id必须与UserMapper中定义的方法名一致。  -->
  <select id="check" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from user
    where username = #{username,jdbcType=VARCHAR}
        and password = #{password,jdbcType=VARCHAR}
  </select>
</mapper>

示例

为了测试整个框架是否已经打好,我们写一个测试页面来测试一下,就用最常用的登录功能来验证。

首先,创建一个User控制器(UserController.java)。

package demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import demo.dao.UserMapper;
import demo.model.User;

@Controller
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/login")
    public String login() {
        return "login";
    }

    @RequestMapping("/dologin")
    public ModelAndView doLogin(String username, String password) {
        User user = this.userMapper.check(username, password);
        if (user == null) {

            return new ModelAndView("error");
        } else {
            ModelAndView modelAndView = new ModelAndView("success");
            modelAndView.addObject("username", username);
            return modelAndView;
        }
    }
}

然后,创建对应的jsp页面来显示结果。

登录页面:login.jsp,文件路径为:src/main/webapp/WEB-INF/jsp/login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>登录</title>
</head>
<body>
    <form action="${pageContext.servletContext.contextPath}/dologin" method="post">
        用户名:<input name="username" type="text" >
        密码:<input name="password" type="password">
        <button type="submit">登录</button>
    </form>
</body>
</html>

登录成功页面:success.jsp,文件路径为:src/main/webapp/WEB-INF/jsp/success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>登录成功</title>
</head>
<body>
    登录成功!您的用户名为:<%=request.getParameter("username")%>
</body>
</html>

登录失败页面:error.jsp,文件路径为:src/main/webapp/WEB-INF/jsp/error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>登录失败</title>
</head>
<body>
    登录失败,用户名或密码错误。
</body>
</html>

使用Jetty启动应用,在浏览器中输入http://localhost:8080/login进入登录界面。如果输入正确的用户名和密码(test/test),则跳入success.jsp对应的页面,输入其他信息,则跳入error.jsp对应的页面。

使用配置文件

通常系统中存在常量(数据库连接URL、用户名、密码等),这些常量如果直接嵌入到Java代码中,则一旦代码编译完毕后就无法更改,如需更改则只能在源代码中改完后重新编译。当然,如果像上面一样,写在诸如applicationContext.xml的xml文件中也是可以的。但是如果这样的文件很多,找起来就会比较困难。

比较推荐的方法是将这些常量集中写到一个常量配置文件中。Java中默认的配置文件后缀是.properties,下面就将常量移入配置文件中。

首先,在applicationContext.xml中加入下面的代码,表示用配置文件中的值来替换掉applicationContext.xml中的变量引用表达式。

<context:property-placeholder location="classpath:config.properties"/>

src/main/resources文件夹中创建config.properties配置文件,加入如下代码:

# ------------------ Data source ----------------
dataSource.pool.driverClass=com.mysql.jdbc.Driver
dataSource.pool.jdbcUrl=jdbc:mysql://localhost:3306/test?characterEncoding=utf8
dataSource.pool.user=test
dataSource.pool.password=test
dataSource.pool.maxPoolSize=10
dataSource.pool.minPoolSize=1
dataSource.pool.initialPoolSize=2
dataSource.pool.maxIdleTime=20
dataSource.pool.acquireIncrement=3
dataSource.pool.idleConnectionTestPeriod=60
dataSource.pool.unreturnedConnectionTimeout=190
dataSource.pool.checkoutTimeout=20000
dataSource.pool.acquireRetryAttempts=30

同时,修改applicationContext.xml文件代码,使用EL表达式来引用这些配置项的值。

<!-- 配置数据连接池(C3P0) -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close">
    <property name="driverClass" value="${dataSource.pool.driverClass}" />
    <property name="jdbcUrl" value="${dataSource.pool.jdbcUrl}" />
    <property name="user" value="${dataSource.pool.user}" />
    <property name="password" value="${dataSource.pool.password}" />
    <property name="maxPoolSize" value="${dataSource.pool.maxPoolSize}" />
    <property name="minPoolSize" value="${dataSource.pool.minPoolSize}" />
    <property name="initialPoolSize" value="${dataSource.pool.initialPoolSize}" />
    <property name="maxIdleTime" value="${dataSource.pool.maxIdleTime}" />
    <property name="acquireIncrement" value="${dataSource.pool.acquireIncrement}" />
    <property name="idleConnectionTestPeriod" value="${dataSource.pool.idleConnectionTestPeriod}" />
    <property name="unreturnedConnectionTimeout" value="${dataSource.pool.unreturnedConnectionTimeout}" />
    <property name="checkoutTimeout" value="${dataSource.pool.checkoutTimeout}" />
    <property name="acquireRetryAttempts" value="${dataSource.pool.acquireRetryAttempts}" />
</bean>

这样在spring容器启动时,会用配置文件中的值替换掉对应的表达式。

单元测试

在Java体系中,JUnit无疑是最强大的单元测试工具,这里就不介绍JUnit的使用了。Spring管理的应用中,依赖关系比较复杂,而且有时会有隐蔽性,单纯的JUnit恐怕很难进行测试。下文介绍专门用于Spring应用单元测试的spring-test组件,以及用于Web应用测试的mock组件的简单使用。

简单的测试

Maven中,通常将所有的测试用例写在src/test/java源文件夹下。下面的代码测试UserMapper这个类,这仅仅是一个样例,介绍如何使用spring-test进行测试而已,测试用例并具有可参考性。

package demo.test.dao;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import demo.dao.UserMapper;
import demo.model.User;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testDoLoginWrong() {
        User user = this.userMapper.check("eroor", "error");
        Assert.assertNull(user);
    }

    @Test
    public void testDoLoginRight() {
        User user = this.userMapper.check("test", "test");
        Assert.assertNotNull(user);
    }
}

web测试

进行web测试时,需要模拟HTTP请求。使用spring-test无需将Web应用部署到服务器就可以进行测试,非常方便实用。下面是一个示例,更多的关于spring-test的知识可以看这篇博文

package demo.test.controller;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

/**
 * 
 * @author xialei
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration("src/main/webapp")
@ContextConfiguration(locations = {"classpath:applicationContext.xml", "file:src/main/webapp/WEB-INF/dispatch-servlet.xml"})
@EnableWebMvc
public class UserControllerTest {

    @Autowired
    private WebApplicationContext wac;

    protected MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }

    @Test
    public void testDoLogin() throws Exception  {
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/dologin").param("username", "test").param("password", "test"))
                .andDo(MockMvcResultHandlers.print()).andReturn();
        String viewName = mvcResult.getModelAndView().getViewName();
        Assert.assertEquals("success", viewName);

        String value = (String)mvcResult.getModelAndView().getModelMap().get("username");
        Assert.assertEquals("test", value);
    }

    @Test
    public void testDoLoginIllegalInput() throws Exception  {
        MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/dologin").param("username", "error").param("password", "error"))
                .andDo(MockMvcResultHandlers.print()).andReturn();
        String viewName = mvcResult.getModelAndView().getViewName();
        Assert.assertEquals("error", viewName);
    }
}

总结

关于使用SpringMVC+Mybatis来创建一个Web应用系列已经写完了。文章从无到有地创建了一个Web应用,知识点讲得不太细,旨在梳理创建整个应用过程中涉及到的概念、组件。阅读本系列文章可以了解到创建一个基于SpringMVC+Mybatis的Web应用是如何一步一步地搭建起来的,虽然示例比较简单,基本停留在Demo级别,但还是比较完整的。在今后创建类似Web应用时,可以直接拿来做代码骨架。


使用springmvc+mybatis创建Web应用(一)—— 相关概念,工具,搭建Web应用

Github地址:https://github.com/xialei199023/springmvc-mybati-webapp


本文由xialei原创,转载请说明出处http://hinylover.space/2016/04/11/springmvc-mybatis-createproject-demo-2/


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值