您的位置:首页 > 编程语言 > Java开发

Spring MVC绑定 List 对象参数

2016-05-24 03:40 651 查看
最近做的一个小小的项目碰上了如何用 post 传递一整个 list 的问题,在解决这个问题的同时,也顺带升级一下 Spring 的版本,并精简一下代码,不过对新的用法没有时间去做太多的探索。

最近也在看 Spring Boot,但是部分配置还没来理解,暂时还没办法用到工程里面来。不多说,一点一点挤牙膏吧。

项目已放到 Github:https://github.com/gaussic/SpringDemo-List

转载请注明出处:Gaussic(一个致力于AI研究却不得不兼顾项目的研究生)

一、开发环境

IntelliJ IDEA 2016.1.2

JDK 1.8.0_92

Tomcat 8.0.35

Maven 3.0.5

Spring 4.2.6.RELEASE

Hibernate 5.1.0.Final

Spring Data JPA 1.10.1.RELEASE

二、配置项目

具体的创建步骤不多说了。

贴出 pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.gaussic</groupId>
<artifactId>springdemo-list</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>springdemo-list Maven Webapp</name>
<url>http://maven.apache.org</url>

<properties>
<spring.version>4.2.6.RELEASE</spring.version>
<hibernate.version>5.1.0.Final</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

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

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.10.1.RELEASE</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>

</dependencies>
<build>
<finalName>springdemo-list</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

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_3_1.xsd" version="3.1">

<display-name>SpringDemo-List Web Application</display-name>

<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>

先把 package 建立好:



配置 mvc-dispatcher-servlet.xml,注意这里把数据库的配置也同样放了进来,所以就不需要 persistence.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:mvc="http://www.springframework.org/schema/mvc" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> 
<context:component-scan base-package="com.gaussic.controller"/>

<mvc:default-servlet-handler/>

<mvc:annotation-driven/>

<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>

<!-- 表示JPA Repository所在的包 -->
<jpa:repositories base-package="com.gaussic.repository"/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="defaultPersistenceUnit"/>
<property name="packagesToScan" value="com.gaussic.model"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
<prop key="hibernate.connection.url">jdbc:mysql://localhost:3306/springdemo-list?useSSL=false</prop>
<prop key="hibernate.connection.username">root</prop>
<prop key="hibernate.connection.password">111111</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.connection.useUnicode">true</prop>
<prop key="hibernate.connection.characterEncoding">UTF-8</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.connection.autoReconnect">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="connection.autoReconnectForPools">true</prop>
<prop key="connection.is-connection-validation-required">true</prop>
</props>
</property>
</bean>

<!-- 事务管理 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<!-- 开启事务管理注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>


三、数据库配置

创建数据库 springdemo-list,utf-8 格式,演示比较简单,只有一张表(主键 id 一定要设置为自增):



构造 UserEntity。

这一步有一点小 trick。因为直接在mvc-dispatcher-servlet.xml里面配置数据库,没有添加 Java EE Persistence 这个 Framework,所以在左下的 Toolbar 无法看到 Persistence 这个选项。怎么做才能把它给调出来呢?

简单的做法是右击项目,添加 Framework:



下拉找到 Java EE Persistence,直接 OK,其他不用配置。



可以发现,resources里面多了 persistence.xml,而左下角多了 Persistence:



这个时候就可以正常地生成 Entity 了:











在 com.gaussic.model 中生成了 UserEntity:

package com.gaussic.model;

import javax.persistence.*;

/**
* Created by dzkan on 2016/5/24.
*/
@Entity
@Table(name = "user", schema = "springdemo-list", catalog = "")
public class UserEntity {
private int id;
private String firstName;
private String lastName;

@Id
@Column(name = "id", nullable = false)
public int getId() {
return id;
}

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

@Basic
@Column(name = "first_name", nullable = true, length = 45)
public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

@Basic
@Column(name = "last_name", nullable = true, length = 45)
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

UserEntity that = (UserEntity) o;

if (id != that.id) return false;
if (firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) return false;
if (lastName != null ? !lastName.equals(that.lastName) : that.lastName != null) return false;

return true;
}

@Override
public int hashCode() {
int result = id;
result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
return result;
}
}

既然 entity 已经生成了,Persistence 就功德圆满了,可以删掉 resources 下的 persistence.xml 了。

四、添加 Repository

刚配置完数据库,现在要配 Repository 了。稍微提一下,JPA 是一种规范,基于这种规范可以非常方便地进行数据库操作,而 Hibernate JPA 实现了这一规范,这也是我们为什么要导入 Hibernate JPA 的原因,导入归导入,但使用的还是 Spring Data JPA 的语法。

新建 UserRepository:

package com.gaussic.repository;

import com.gaussic.model.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;

/**
* Created by dzkan on 2016/5/24.
*/
public interface UserRepository extends JpaRepository<UserEntity, Integer> {

// 由于后面要实现一个简单的查重,需要这个方法,查询指定姓和名的人的个数
Long countByFirstNameAndLastName(String firstName, String lastName);
}


五、添加 Controller

1、首页

我们想在首页先试一下数据库中有多少个用户。

新建 DemoController:

package com.gaussic.controller;

import com.gaussic.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* Created by dzkan on 2016/5/24.
*/
@Controller
public class DemoController {

@Autowired
UserRepository userRepository;

// 首页
@RequestMapping("/")
public String index(ModelMap model) {
model.addAttribute("user_cnt", userRepository.count());
return "index";
}
}

在 WEB-INF 下新建 pages 文件夹,把 index.jsp 拖到这个文件夹下:



修改 index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>用户导入系统首页</title>

<!-- Bootstrap -->
<link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>

<div class="container">
<h3>用户导入系统首页</h3>
<hr style="border-color: steelblue"/>
<h4>数据库中已有 <span style="color: red">${user_cnt}</span> 名用户 。</h4>
<br/>
<br/>
<a href="${pageContext.request.contextPath}/users" type="button" class="btn btn-success">进入用户系统</a>
</div>

<script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>

配置 Tomcat,部署并运行(不再赘述):



2、批量输入数据

接下来就要完成我的下一个目标,如何批量的输入数据,即一次性输入多个用户。

首先,要找个方法把多个用户用列表封装起来。在 com.gaussic.model 下新建一个 UserListForm,这个类非常简单,只有一个 list 及其 get 和 set 方法:

package com.gaussic.model;

import java.util.List;

/**
* Created by dzkan on 2016/5/24.
*/
public class UserListForm {

private List<UserEntity> users;

public List<UserEntity> getUsers() {
return users;
}

public void setUsers(List<UserEntity> users) {
this.users = users;
}
}

在 DemoController 中添加方法,映射到批量添加用户页面:

// 指向批量添加用户页面
@RequestMapping("/users")
public String users() {
return "users";
}

在 pages 下新建页面 users.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>用户输入</title>

<link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>

<div class="container">
<h3>请输入用户姓名</h3>
<hr style="border-color: steelblue"/>

<form class="form-horizontal" action="${pageContext.request.contextPath}/addUser" method="post">
<div class="question-list panel panel-success" style="padding:10px; font-size: 16px;">
<c:forEach var="i" begin="1" end="3">
<div class="form-group">
<label for="firstName${i}" class="col-md-2 control-label">First Name ${i}:</label>
<div class="col-md-10">
<input type="text" class="form-control" id="firstName${i}" name="users[${i-1}].firstName"
required="required">
</div>
</div>
<div class="form-group">
<label for="lastName${i}" class="col-md-2 control-label">Last Name ${i}:</label>
<div class="col-md-10">
<input type="text" class="form-control required" id="lastName${i}" name="users[${i-1}].lastName"
required="required">
</div>
</div>
</c:forEach>
</div>

<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" class="btn btn-info" id="addBtn">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</div>
</div>

<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">提交</button>
</div>
</div>
</form>
</div>

<script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>
$("#addBtn").click(function () {
var user_num = $(".question-list .form-group").length / 2;
if (user_num < 5) {
var firstName = "<div class=\"form-group\">"
+ "<label for=\"firstName" + (user_num + 1) + "\" class=\"col-md-2 control-label\">First Name " + (user_num + 1) + ":</label>"
+ "<div class=\"col-md-10\">"
+ "<input type=\"text\" class=\"form-control\" id=\"firstName" + (user_num + 1) + "\" name=\"users[" + (user_num) + "].firstName\" required=\"required\">"
+ "</div></div>";

var lastName = "<div class=\"form-group\">"
+ "<label for=\"lastName" + (user_num + 1) + "\" class=\"col-md-2 control-label\">Last Name " + (user_num + 1) + ":</label>"
+ "<div class=\"col-md-10\">"
+ "<input type=\"text\" class=\"form-control\" id=\"lastName" + (user_num + 1) + "\" name=\"users[" + (user_num) + "].lastName\" required=\"required\">"
+ "</div></div>";

$(".question-list").append(firstName + lastName);
} else {
alert("最多只能输入 5 个用户,若想输入更多,请在提交本页后重新进入。");
}
});
</script>
</body>
</html>

注意以下几点,其中 name 中的参数要严格按照 UserListForm 的参数名:



看看效果:



这里多了一个挺好玩的小功能,就是在数量不够的手动添加。页面已经做好了,现在要实现它的 post 方法了:

在 DemoController 中添加如下方法:

// 添加用户,post 请求
@RequestMapping(value = "/addUser", method = RequestMethod.POST)
public String addUser(UserListForm userListForm, ModelMap model) {

List<UserEntity> succeedList = new ArrayList<>();  // 添加成功
List<UserEntity> failList = new ArrayList<>();     // 添加不成功
for(UserEntity user : userListForm.getUsers()) {
// 基本查重
if(userRepository.countByFirstNameAndLastName(user.getFirstName(), user.getLastName()) == 0) {
userRepository.saveAndFlush(user);
succeedList.add(user);
} else {
failList.add(user);
}
}

model.addAttribute("succeedList", succeedList);
model.addAttribute("failList", failList);
return "user_list";
}

然后,创建一个页面,返插入结果:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>用户输入结果</title>

<link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/custome.css">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>

<div class="container">
<h3>用户输入结果列表</h3>
<hr style="border-color: steelblue"/>

<div class="question-list panel panel-success" style="padding:10px;">
<h3>以下是输入成功的用户,数量 <span style="color: red">${succeedList.size()}</span></h3>
<c:forEach var="i" begin="1" end="${succeedList.size()}">
<h4 style="padding: 5px;">First Name ${i}: ${succeedList[i-1].firstName}</h4>
<h4 style="padding: 5px;">Last Name ${i}: ${succeedList[i-1].lastName}</h4>
</c:forEach>
</div>

<div class="question-list panel panel-success" style="padding:10px;">
<h3>以下用户库中已存在,数量 <span style="color: red">${failList.size()}</span></h3>
<c:forEach var="i" begin="1" end="${failList.size()}">
<h4 style="padding: 5px;">First Name ${i}: ${failList[i-1].firstName}</h4>
<h4 style="padding: 5px;">Last Name ${i}: ${failList[i-1].lastName}</h4>
</c:forEach>
</div>

<div class="row" style="margin-bottom: 50px;">
<div class="col-sm-offset-5 col-sm-7">
<a type="button" href="${pageContext.request.contextPath}/users" class="btn btn-info"
style="font-weight: bold; margin-bottom: 5px;">继续输入</a>
<a type="button" href="${pageContext.request.contextPath}/" class="btn btn-info"
style="font-weight: bold; margin-bottom: 5px;">返回首页</a>
</div>
</div>
</div>

<script src="http://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</body>
</html>

看看效果:



任务完成,其他的任务就不加进来了。按道理说,这个项目稍微改改就可以自己用了,可以省去不少配置的时间。

项目已放到 Github:https://github.com/gaussic/SpringDemo-List

转载请注明出处:Gaussic(一个致力于AI研究却不得不兼顾项目的研究生)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring MVC Data JPA