JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC
接上篇《java web快速入门之通过一个简单的spring项目了解spring的核心(aop、ioc)》,了解了spring的核心(aop、ioc)后,我们再来学习与实践maven、springmvc、springjdbc(即:ssm中的s(spring)s(springmvc)),暂不涉及orm部份(即:m(mybatis)),mybatis将在下一篇文章中继续给大家分享。我相信通过之前几篇文章的学习与实践,已基本熟悉了搭建jsp网站及把aop ioc应用到项目中,已具备编写jsp 普通web网站了,故从本文开始,一些之前讲述过的步骤不再一一说明,只讲重点及结果。
(提示:本文内容有点长,涉及的知识点也比较多,若是新手建议耐心看完!)
一、了解maven并基于maven构建一个空的springmvc网站:
1.1maven是什么?
maven 翻译为"专家"、"内行",是 apache 下的一个纯 java 开发的开源项目。基于项目对象模型(缩写:pom)概念,maven利用一个*信息片断能管理一个项目的构建、报告和文档等步骤。
主要功能有:构建、文档生成、报告、依赖、scms、发布、分发、邮件列表
maven 提倡使用一个共同的标准目录结构,maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构,如下图示(来源网络):(假定 ${basedir} 表示工程目录)
maven 有以下三个标准的生命周期:(每个生命周期中都包含着一系列的阶段(phase)。这些 phase 就相当于 maven 提供的统一的接口,然后这些 phase 的实现由 maven 的插件来完成。)
clean:项目清理的处理 、default(或 build):项目部署的处理、site:项目站点文档创建的处理
详细说明请参见:
1.2搭建maven环境
1.2.1 从官网下载地址: 中选择对应版本点击下载(开发一般以windows为主,故下载apache-maven-x.x.x-bin.zip),下载后解压到对应的目录,然后配置如下环境变量:
新增变量名: maven_home,变量值:maven解压根目录
编辑系统变量 path,添加变量值:%maven_home%\bin (win10系统直接添加一行)
配置完后,在cmd中输入:mvn -v 如果正常输出maven版本信息则表示ok;
1.2.2 设置maven的本地仓库的下载位置(默认是在系统盘(一般为c):\users\当前用户名\.m2\repository),如果不改变则会导致系统盘分区容量不足,建议及时修改
打开%maven_home%\conf 目录下的settings.xml,修改localrepository元素的内容为自定义的repository目录,如:
<localrepository>e:/localmvnrepositories</localrepository>
然后设置ide(eclipse或idea)maven的存储位置,如下是eclipse的修改截图,idea同理
依次打开:windows->perferences->maven(右边列表节点)->user settings,修改global settings 、user settings的路径(将settings.xml copy到指定的目录,然后这里设置这个目录),如下图示:【若没有maven选项可能需要手动安装maven插件,详见:】
点击update settings按钮完成更新,最后apply即可
1.2.3 settings.xml中设置镜像*仓库url,如下:(阿里云仓库)
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorof>central</mirrorof>
</mirror>
</mirrors>
1.2.4 使用maven命令创建一个空的maven webapp,在cmd中执行如下命令:(请先cd切换到指定的项目创建根目录再执行如下命令)
mvn archetype:generate
然后根据每步提示进行交互处理,一般依次输入:archetypeartifactid(如果要搭建mvc,则选择maven-archetype-webapp)、gourpid、artifactid、version、package,如下图示:
当然如果不想一步步的按照向导来操作,可以带上完整参数来进行操作,例如:
mvn archetype:generate
-dgroupid=cn.zuowenjun.java
-dartifactid=mvnspringmvc2
-darchetypeartifactid=maven-archetype-webapp
-dinteractivemode=false
执行完成后会输出build success字样,就表示构建maven webapp项目成功,本地文件目录就会生成相关的文件,如果需要使用ide打开,可以在eclipse中通过:file->open projects from file systm or archive->选择import source路径(命令生成的项目根目录)->finish即可。
1.2.5 通过ide(eclipse) maven插件来构建项目,操作步骤为:
file->new->maven project ->向导界面(create a simple proejct不要勾选,其余项按需设置)->向导界面(select an archetype:选择maven-archetype-webapp,如下图示)->设置->finish即可
如果发现archetype(即:项目原型模板,与vs中创建某个项目类似)的version比较低,可以使用右下角的"add archetype"添加最新的archetype(如上图示出现的1.4版本就是我加的),这里我们仍选择1.0,下一步就出现如下图示,设置相关信息即可
两个踩坑点:(不论是用mvn命令行还是maven插件创建的webapp项目存在两个问题,需要处理)
1.构建webapp项目资源目录显示不全,缺少java等目录,问题原因是默认的项目是使用的jre1.5,我们只需改成当前最新的版本即可(如:1.8),参考:https://blog.csdn.net/sunny1994_/article/details/79058685
2.改完后可能还会报:description resource path location type java compiler level does not match the version of the installed java project facet. mvnspringmvc unknown faceted project problem (java version mismatch),这是由于编译的jdk版本与项目的jdk版本不一致造成的,通过:在项目上右键properties-》project facets,在打开的project facets页面中的java下拉列表中,选择相应版本,如下图示:
3.报缺少javax.servlet.http.httpservlet父类,需要在pom文件中添加一下jar包依赖,配置如下:(version请按需要配置)
<dependencies>
<dependency>
<groupid>javax.servlet</groupid>
<artifactid>javax.servlet-api</artifactid>
<version>3.1.0</version>
</dependency>
</dependencies>
解决后,最后再buid proejct如无报错,说明空的maven project已创建ok。
1.3添加springmvc、springjdbc相关依赖、配置web.xml,实现springmvc项目开发环境
1.3.1在pom.xml中添加springmvc、springjdbc依赖,如下:(这里将版本号单独统一配置在properties中)
<properties>
<spring.version>5.1.3.release</spring.version>
</properties>
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>javax.servlet</groupid>
<artifactid>javax.servlet-api</artifactid>
<version>3.1.0</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-core</artifactid>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-web</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-jdbc</artifactid>
<version>${spring.version}</version>
</dependency>
</dependencies>
如下是完整示例pom文件内容:(比较完整,涉及springmvc、springjdbc、数据驱动【这里是sqlserver】、jsp视图【jstl、el】)
<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>cn.zuowenjun.java</groupid>
<artifactid>mvnspringmvc</artifactid>
<packaging>war</packaging>
<version>0.0.1-snapshot</version>
<name>mvnspringmvc maven webapp</name>
<url>http://maven.apache.org</url>
<properties>
<spring.version>5.1.3.release</spring.version>
</properties>
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- jsp视图所需依赖 -->
<dependency>
<groupid>javax.servlet</groupid>
<artifactid>javax.servlet-api</artifactid>
<version>3.1.0</version>
</dependency>
<dependency>
<groupid>javax.servlet.jsp</groupid>
<artifactid>jsp-api</artifactid>
<version>2.2</version>
</dependency>
<!-- jsp jstl所需依赖 -->
<dependency>
<groupid>javax.servlet.jsp.jstl</groupid>
<artifactid>jstl-api</artifactid>
<version>1.2</version>
<exclusions>
<exclusion>
<groupid>javax.servlet</groupid>
<artifactid>servlet-api</artifactid>
</exclusion>
<exclusion>
<groupid>javax.servlet.jsp</groupid>
<artifactid>jsp-api</artifactid>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupid>org.glassfish.web</groupid>
<artifactid>jstl-impl</artifactid>
<version>1.2</version>
<exclusions>
<exclusion>
<groupid>javax.servlet</groupid>
<artifactid>servlet-api</artifactid>
</exclusion>
<exclusion>
<groupid>javax.servlet.jsp</groupid>
<artifactid>jsp-api</artifactid>
</exclusion>
<exclusion>
<groupid>javax.servlet.jsp.jstl</groupid>
<artifactid>jstl-api</artifactid>
</exclusion>
</exclusions>
</dependency>
<!-- springmvc所需依赖 -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-core</artifactid>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-web</artifactid>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-webmvc</artifactid>
<version>${spring.version}</version>
</dependency>
<!-- spring jdbc【数据访问】所需依赖 -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-jdbc</artifactid>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupid>com.microsoft.sqlserver</groupid>
<artifactid>mssql-jdbc</artifactid>
<version>7.0.0.jre8</version>
</dependency>
</dependencies>
<build>
<finalname>mvnspringmvc</finalname>
</build>
</project>
1.3.2在web.xml中配置dispatcherservlet及contextloaderlistener,如下:(如下完整的web.xml,有改造过,因为默认的web的声明头有问题,另外如果不配置contextconfiglocation,那么springcontext的配置文件默认路径:[servlet-name(是dispatcherservlet配置的名称)]-servlet.xml)
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
xsi:schemalocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>archetype created web application</display-name>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class>
<init-param>
<param-name>contextconfiglocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- map all requests to the dispatcherservlet for handling -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextconfiglocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.contextloaderlistener</listener-class>
</listener>
<!-- 解决中文请求乱码问题 -->
<filter>
<filter-name>characterencoding</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>characterencoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 定义默认首页,欢迎页 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 定义错误处理页面,此处只定义404,其余的通过@controlleradvice+@exceptionhandler来处理 -->
<error-page>
<error-code>404</error-code>
<location>/web-inf/errors/notfound.jsp</location>
</error-page>
</web-app>
知识扩展说明:springmvc异常统一处理有多种方式,可参见:https://www.cnblogs.com/bloodhunter/p/4825279.html 、 https://www.cnblogs.com/junzi2099/p/7840294.html 、https://blog.csdn.net/butioy_org/article/details/78718405
1.3.3在src/main/resources中创建springmvc-servlet.xml(即:contextconfiglocation配置的路径文件),这个是spring context配置文件与上一篇介绍的beans.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:tx="http://www.springframework.org/schema/tx"
xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 对包中的所有类进行扫描,以完成bean创建和自动依赖注入的功能 ,多个包名用逗号分隔 -->
<context:component-scan
base-package="cn.zuowenjun.java.mvc"></context:component-scan>
<mvc:annotation-driven>
<!-- <mvc:argument-resolvers></mvc:argument-resolvers> -->
<!-- <mvc:async-support></mvc:async-support> -->
<!-- <mvc:message-converters></mvc:message-converters> -->
<!-- <mvc:path-matching/> -->
<!-- <mvc:return-value-handlers></mvc:return-value-handlers> -->
</mvc:annotation-driven>
<bean
class="org.springframework.web.servlet.view.internalresourceviewresolver">
<property name="prefix" value="/web-inf/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 配置静态资源,如html,图片,js,css等,多个资源位置可用逗号分隔 -->
<mvc:resources mapping="/pages/**" location="/web-inf/pages/" />
<!-- 可配置读取外部配置文件,如果有配置jdbc.properties,则下面的datasource的相关property可以使用${xxx}占位符,这里不演示 -->
<!--<context:property-placeholder location="classpath:jdbc.properties" /> -->
<bean id="datasource" class="org.springframework.jdbc.datasource.drivermanagerdatasource">
<property name="driverclassname" value="com.microsoft.sqlserver.jdbc.sqlserverdriver"></property>
<property name="url" value="jdbc:sqlserver://ip:port;databasename=testdb"></property>
<property name="username" value="dbuser"></property>
<property name="password" value="password"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="txmanager"
class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
<property name="datasource" ref="datasource" />
</bean>
<!-- 事务扫描开始(开启@tranctional) 此示例暂不启用-->
<!-- <tx:annotation-driven transaction-manager="txmanager" /> -->
</beans>
对如下配置作简要说明:
context:component-scan:自动扫描包并将标记@component(组件),@service(服务),@controller(控制器),@repository(数据仓库)的bean注册到spring ioc容器中,这样就无需手动在xml进行单独配置了;详见:
mvc:annotation-driven:简化配置自动注册defaultannotationhandlermapping与annotationmethodhandleradapter两个bean,这些是spring mvc为@controller分发请求所必须的。里面也包含一些子元素的配置,详见:
mvc:resources:配置处理静态资源,配置的url路径将只会由default默认的servlet来处理,而不再由dispatcherservlet处理,详见:
注册bean:internalresourceviewresolver,视图解析器,用于找到视图文件;
注册bean:drivermanagerdatasource,指定springjdbc的数据源驱动相关连接信息;
注册bean:datasourcetransactionmanager,配置事务管理器,用于事务处理;
如上步骤都操作完后,就可以buid project,如果构建成功,则说明springmvc项目环境已ok;可以看到项目目录如下图示:
(其中那些包都是我为后面写代码时提前创建的)
二、编写springmvc示例代码(演示:发博文,写评论),了解springmvc及springjdbc:
2.1.定义model:(在cn.zuowenjun.java.mvc.model包中分类定义:post、postcomment 两个数据模型)
//post.java
package cn.zuowenjun.java.mvc.model;
import java.util.date;
public class post {
private int id;
private string title;
private string content;
private string author;
private date createtime;
public int getid() {
return id;
}
public void setid(int id) {
this.id = id;
}
public string gettitle() {
return title;
}
public void settitle(string title) {
this.title = title;
}
public string getcontent() {
return content;
}
public void setcontent(string content) {
this.content = content;
}
public string getauthor() {
return author;
}
public void setauthor(string author) {
this.author = author;
}
public date getcreatetime() {
return createtime;
}
public void setcreatetime(date createtime) {
this.createtime = createtime;
}
}
//postcomment.java
package cn.zuowenjun.java.mvc.model;
import java.util.date;
public class postcomment {
private int id;
private int postid;
private string content;
private string createby;
private date createtime;
public int getid() {
return id;
}
public void setid(int id) {
this.id = id;
}
public int getpostid() {
return postid;
}
public void setpostid(int postid) {
this.postid = postid;
}
public string getcontent() {
return content;
}
public void setcontent(string content) {
this.content = content;
}
public string getcreateby() {
return createby;
}
public void setcreateby(string createby) {
this.createby = createby;
}
public date getcreatetime() {
return createtime;
}
public void setcreatetime(date createtime) {
this.createtime = createtime;
}
}
非常简单的pojo风格的两个类,只有成对的getter、setter方法。
2.2定义dao接口:(在cn.zuowenjun.java.mvc.dao包中分别定义:postdao、postcommentdao 两个dao接口,注意java与c#的接口定义规范有所不同,c#中要求以i开头,而java中并没有此要求,java只要要求实现类以impl后缀结尾)
//postdao.java
package cn.zuowenjun.java.mvc.dao;
import java.util.date;
import java.util.list;
import cn.zuowenjun.java.mvc.model.post;
public interface postdao {
post get(int id);
list<post> getlist(date frmdate,date todate);
int create(post post);
boolean delete(int id);
boolean update(post post);
}
//postcommentdao.java
package cn.zuowenjun.java.mvc.dao;
import java.util.list;
import cn.zuowenjun.java.mvc.model.postcomment;
public interface postcommentdao {
postcomment get(int id);
list<postcomment> getlist(int postid);
boolean create(postcomment postcmmt);
boolean delete(int id);
boolean update(postcomment postcmmt);
}
如上代码所示,dao接口主要定义了增、删、改、查(查单个,查多个),这是大部份的常见场景,根据需要定义。
2.3实现dao接口:(在cn.zuowenjun.java.mvc.dao.impl包中分别定义:basedao、postdaoimpl、postcommentdaoimpl,其中basedao是抽像类,主要是约束构造函数的入参及提供通用的获取jdbctemplate实例对象)
basedao抽象类定义:
package cn.zuowenjun.java.mvc.dao.impl;
import javax.sql.datasource;
import org.springframework.jdbc.core.jdbctemplate;
import org.springframework.jdbc.core.namedparam.namedparameterjdbctemplate;
public abstract class basedao {
private jdbctemplate jdbctemplateobj;
private namedparameterjdbctemplate namedparamjdbctemplate;
public basedao(datasource datasource) {
this.jdbctemplateobj=new jdbctemplate(datasource);
this.namedparamjdbctemplate=new namedparameterjdbctemplate(datasource);
}
protected jdbctemplate getjdbctemplate() {
return this.jdbctemplateobj;
}
protected namedparameterjdbctemplate getnamedparameterjdbctemplate() {
return this.namedparamjdbctemplate;
}
}
basedao抽象类中分别实例化了两个数据访问对象:jdbctemplate、namedparameterjdbctemplate,之所以这样做是因为我会分别在postdaoimpl演示使用namedparameterjdbctemplate来进行crud操作,postcommentdaoimpl演示使用jdbctemplate来进行crud操作。通过对比代码来发现两者的区别以及优势。
postdaoimpl类定义:
package cn.zuowenjun.java.mvc.dao.impl;
import java.util.*;
import javax.sql.datasource;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.dao.dataaccessexception;
import org.springframework.jdbc.core.beanpropertyrowmapper;
import org.springframework.jdbc.core.namedparam.*;
import org.springframework.jdbc.support.*;
import org.springframework.transaction.*;
import org.springframework.transaction.support.*;
import cn.zuowenjun.java.mvc.dao.*;
import cn.zuowenjun.java.mvc.model.post;
public class postdaoimpl extends basedao implements postdao {
private platformtransactionmanager transactionmanager;
@autowired
public postdaoimpl(datasource datasource,platformtransactionmanager transactionmanager) {
super(datasource);
this.transactionmanager=transactionmanager;
}
@override
public post get(int id) {
string sql="select * from ta_testpost where id=:id";
mapsqlparametersource mapsqlparametersource = new mapsqlparametersource();
mapsqlparametersource.addvalue("id", id);
return this.getnamedparameterjdbctemplate().queryforobject(sql, mapsqlparametersource,new beanpropertyrowmapper<>(post.class));
}
@override
public list<post> getlist(date frmdate, date todate) {
string sql="select * from ta_testpost where createtime between :frmdate to :todate";
map<string, object> parammap = new hashmap<>();
parammap.put("frmdate", frmdate);
parammap.put("todate", todate);
return this.getnamedparameterjdbctemplate().query(sql, parammap,new beanpropertyrowmapper<>(post.class));
}
@override
public int create(post post) {
string sql="insert into ta_testpost(title, content, author, createtime) values(:title,:content,:author,getdate())";
beanpropertysqlparametersource beanpropparam=new beanpropertysqlparametersource(post);
keyholder keyholder = new generatedkeyholder();
int r= this.getnamedparameterjdbctemplate().update(sql, beanpropparam, keyholder);
if(r>0) {
system.out.println("create is ok!");
return keyholder.getkey().intvalue();
}
else {
system.out.println("create is failed!");
return -1;
}
}
@override
public boolean delete(int id) {
transactiondefinition def = new defaulttransactiondefinition();
transactionstatus status = transactionmanager.gettransaction(def);
try {
map<string, object> parammap = new hashmap<>();
parammap.put("postid",id);
namedparameterjdbctemplate namedjdbctemplate=this.getnamedparameterjdbctemplate();
namedjdbctemplate.update("delete from ta_testpost where id=:postid",parammap);
namedjdbctemplate.update("delete from ta_testpostcomment where postid=:postid", parammap);
transactionmanager.commit(status);
system.out.println("delete is ok!");
return true;
}catch(dataaccessexception daex) {
system.out.printf("delete is failed,exception:%s",daex.getmessage());
transactionmanager.rollback(status);
return false;
}
}
@override
public boolean update(post post) {
string sql="update ta_testpost set title=:title,content=:content,author=:author,createtime=getdate() where id=:id";
beanpropertysqlparametersource beanpropparam=new beanpropertysqlparametersource(post);
int r= this.getnamedparameterjdbctemplate().update(sql, beanpropparam);
if(r>0) {
system.out.println("update is ok!");
return true;
}
else {
system.out.println("update is failed!");
return false;
}
}
}
涉及知识要点说明:
1.namedparameterjdbctemplate支持的常用sql参数类型有:mapsqlparametersource(键值对),map<string, ?>,beanpropertysqlparametersource(把一个bean所有属性映射成参数,推荐这种)
2.namedparameterjdbctemplate 使用的sql语句中参数的命名规则为:冒号+参数名,如: :content,有点类似c#中的ado.net的参数化类型(@content),只是前缀指示符不同而矣。
3.若执行新增一条记录,且需要返回自增的id,这时可以通过传入generatedkeyholder的实例,最后使用该实例的变量获取自增id,如上面的create方法中的一样,最终使用:keyholder.getkey().intvalue()获取到自增id;
4.查询返回的结果若要映射为实体对象(pojo、javabean),则需要自定义实现rowmapper<t>,然后传入自定义的rowmapper的变量,当然可以使用spring jdbc 的默认实现类:beanpropertyrowmapper(内部使用反射, 可能在某些场景下性能不佳),如果只需要返回某列的值,则可以直接指定映射的类型,如:string.class
5.若想查询返回一个实体对象或实体对象列表,应该使用queryforobject、query的重载方法含rowmapper<t>的参数,注意:queryforlist 只能返回某一列的值,不能直接返回实体对象列表,因为最后的一个参数class<t> requiredtype最终内部转化为了:singlecolumnrowmapper
6.使用事务需要platformtransactionmanager、transactiondefinition、transactionstatus,如上面的delete方法;
postcommentdaoimpl类定义:
package cn.zuowenjun.java.mvc.dao.impl;
import java.sql.resultset;
import java.sql.sqlexception;
import java.util.list;
import javax.sql.datasource;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.jdbc.core.rowmapper;
import cn.zuowenjun.java.mvc.dao.postcommentdao;
import cn.zuowenjun.java.mvc.model.postcomment;
import cn.zuowenjun.java.mvc.model.mapper.postcommentmapper;
public class postcommentdaoimpl extends basedao implements postcommentdao {
@autowired
public postcommentdaoimpl(datasource datasource) {
super(datasource);
// todo auto-generated constructor stub
}
@override
public postcomment get(int id) {
string sql = "select * from ta_testpostcomment where id=?";
// 第一种:基于定义好的实现了rowmapper的postcommentmapper实例
// return this.getjdbctemplate().queryforobject(sql,new object[] {id},new
// postcommentmapper());
// 第二种:直接使用匿名内部类(可以理解为与c#的委托类型)
// return this.getjdbctemplate().queryforobject(sql,new object[]{id},new rowmapper<postcomment>() {
//
// @override
// public postcomment maprow(resultset rs, int rownum) throws sqlexception {
// postcomment model=new postcomment();
// model.setid(rs.getint("id"));
// model.setpostid(rs.getint("postid"));
// model.setcontent(rs.getstring("content"));
// model.setcreateby(rs.getstring("createby"));
// model.setcreatetime(rs.getdate("createtime"));
// return model;
// }
// });
// 第三种:直接使用lambda表达式,这个与c#lambda就比较像了,因为java抄自c#
return this.getjdbctemplate().queryforobject(sql, new object[] { id }, (rs, i) -> {
postcomment model = new postcomment();
model.setid(rs.getint("id"));
model.setpostid(rs.getint("postid"));
model.setcontent(rs.getstring("content"));
model.setcreateby(rs.getstring("createby"));
model.setcreatetime(rs.getdate("createtime"));
return model;
});
}
@override
public list<postcomment> getlist(int postid) {
string sql = "select * from ta_testpostcomment where postid=?";
return this.getjdbctemplate().query(sql, new object[] { postid }, new postcommentmapper());
}
@override
public boolean create(postcomment postcmmt) {
string sql = "insert into ta_testpostcomment(id, postid, content, createby, createtime) values(?,?,?,?,?)";
int r = this.getjdbctemplate().update(sql, new object[] { postcmmt.getid(), postcmmt.getpostid(),
postcmmt.getcontent(), postcmmt.getcreateby(), postcmmt.getcreatetime() });
if (r > 0) {
system.out.println("create is ok!");
return true;
} else {
system.out.println("create is failed!");
return false;
}
}
@override
public boolean delete(int id) {
string sql = "delete from ta_testpostcomment where id=?";
int r = this.getjdbctemplate().update(sql, new object[] { id });
if (r > 0) {
system.out.println("delete is ok!");
return true;
} else {
system.out.println("delete is failed!");
return false;
}
}
@override
public boolean update(postcomment postcmmt) {
string sql = "update ta_testpostcomment set postid=?,content=?,createby=?,createtime=getdate() where id=?";
int r = this.getjdbctemplate().update(sql, (pss) -> {
pss.setint(1, postcmmt.getpostid());
pss.setstring(2, postcmmt.getcontent());
pss.setstring(3, postcmmt.getcreateby());
pss.setint(4, postcmmt.getid());
});
if (r > 0) {
system.out.println("update is ok!");
return true;
} else {
system.out.println("update is failed!");
return false;
}
}
}
postcommentmapper类定义:
package cn.zuowenjun.java.mvc.model.mapper;
import java.sql.resultset;
import java.sql.sqlexception;
import org.springframework.jdbc.core.rowmapper;
import cn.zuowenjun.java.mvc.model.postcomment;
public class postcommentmapper implements rowmapper<postcomment> {
@override
public postcomment maprow(resultset rs, int rownum) throws sqlexception {
postcomment model=new postcomment();
model.setid(rs.getint("id"));
model.setpostid(rs.getint("postid"));
model.setcontent(rs.getstring("content"));
model.setcreateby(rs.getstring("createby"));
model.setcreatetime(rs.getdate("createtime"));
return model;
}
}
涉及知识要点说明:
1.jdbctemplate支持的常见sql参数类型有:object[](namedparameterjdbctemplate不直接支持,可通过getjdbctemplate获得jdbctemplate,然后继续使用jdbctemplate的crud用法),preparedstatementcreator、preparedstatementsetter
2.jdbctemplate返回结果常见处理转换类型有:rowmapper<t>、rowcallbackhandler、resultsetextractor<t> ,注意涉及数据索引都是从1开始
3.无论是namedparameterjdbctemplate 还是jdbctemplate的入参sql参数类型、返回结果参数处理类型大部份都是函数式接口(标注了@functionalinterface),意味着我们可以直接使用匿名内部类或lambda表达式来传参,如上面代码中的get方法,分别演示了使用postcommentmapper、new rowmapper<postcomment>(){...}匿名内部类、(rs, i) -> {...}lambda表达式,其它同理;
4.jdbctemplate的sql语句中的参数命名规则为:?,与使用原生的jdbc 进行参数化查询用法相同
namedparameterjdbctemplate 与jdbctemplate 都实现jdbcoperations接口;
2.4定义service接口:(在cn.zuowenjun.java.mvc.service包中分别定义了:postservice、postcommentservice、userservice 三个接口)
//postservice.java
package cn.zuowenjun.java.mvc.service;
import java.util.date;
import java.util.list;
import cn.zuowenjun.java.mvc.model.post;
public interface postservice {
post get(int id);
list<post> getlist(date frmdate,date todate);
list<post> getall();
int create(post post);
boolean delete(int id);
boolean update(post post);
}
//postcommentservice.java
package cn.zuowenjun.java.mvc.service;
import java.util.list;
import cn.zuowenjun.java.mvc.model.postcomment;
public interface postcommentservice {
postcomment get(int id);
list<postcomment> getlist(int postid);
boolean create(postcomment postcmmt);
boolean delete(int id);
boolean update(postcomment postcmmt);
}
//userservice .java
package cn.zuowenjun.java.mvc.service;
public interface userservice {
string login(string uid,string pwd);
string logout();
string getloginusername();
}
如上代码所示,接口中只是定义了业务所需要的方法,可能大家看到觉得与dao接口很类似(示例代码中确实是相同的),但其实它们服务的对象不同,dao可能相比service更原子化,更单一 一些,dao只需要为单个表提供数据读写服务,而service则是为表现层(ui)提供真实的服务场景,而这些场景有可能是复杂的,包含多个dao的数据源或操作多个dao,同时还承担了业务数据的验证逻辑处理转换等功能,service层我理解是业务服务层(bll),如果按照ddd来划分,我认为service层则是应用服务层或领域服务层
2.5实现service接口:(在cn.zuowenjun.java.mvc.service.impl包中分别定义postserviceimpl、postcommentserviceimpl、userserviceimpl类,它们实现了对应的service接口)
//postserviceimpl.java
package cn.zuowenjun.java.mvc.service.impl;
import java.security.*;
import java.text.parseexception;
import java.text.simpledateformat;
import java.util.date;
import java.util.list;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import cn.zuowenjun.java.mvc.model.post;
import cn.zuowenjun.java.mvc.service.postservice;
import cn.zuowenjun.java.mvc.dao.*;
@service
public class postserviceimpl implements postservice {
@autowired
private postdao postdao;
@override
public post get(int id) {
return postdao.get(id);
}
@override
public list<post> getlist(date frmdate, date todate) {
long frmdateval=frmdate.gettime();
long todateval=todate.gettime();
if(frmdateval>todateval) {
throw new invalidparameterexception("开始时间需<=结束时间");
}
return postdao.getlist(frmdate, todate);
}
@override
public list<post> getall() {
simpledateformat sdf = new simpledateformat("yyyy-mm-dd");
try {
return getlist(sdf.parse("1900-1-1"), sdf.parse("2099-12-1"));
} catch (parseexception e) {
return null;
}
}
@override
public int create(post post) {
string result=verifymodel(post,true);
if(!result.isempty()) {
throw new invalidparameterexception(result);
}
return postdao.create(post);
}
@override
public boolean delete(int id) {
return postdao.delete(id);
}
@override
public boolean update(post post) {
string result=verifymodel(post,true);
if(!result.isempty()) {
throw new invalidparameterexception(result);
}
return postdao.update(post);
}
private string verifymodel(post post,boolean isnew) {
stringbuilder errmsgbuilder=new stringbuilder();
if(!isnew && post.getid()<=0) {
errmsgbuilder.append("id不能为空!");
}
if(post.gettitle().trim().isempty()) {
errmsgbuilder.append("标题不能为空!");
}
if(post.getcontent().trim().isempty()) {
errmsgbuilder.append("内容不能为空!");
}
if(post.getauthor().trim().isempty()) {
errmsgbuilder.append("作者不能为空!");
}
return errmsgbuilder.tostring();
}
}
//postcommentserviceimpl.java
package cn.zuowenjun.java.mvc.service.impl;
import java.security.invalidparameterexception;
import java.util.date;
import java.util.list;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import cn.zuowenjun.java.mvc.dao.postcommentdao;
import cn.zuowenjun.java.mvc.model.postcomment;
import cn.zuowenjun.java.mvc.service.postcommentservice;
@service
public class postcommentserviceimpl implements postcommentservice {
@autowired
private postcommentdao postcommentdao;
@override
public postcomment get(int id) {
return postcommentdao.get(id);
}
@override
public list<postcomment> getlist(int postid) {
return postcommentdao.getlist(postid);
}
@override
public boolean create(postcomment postcmmt) {
string result=verifymodel(postcmmt,true);
if(!result.isempty()) {
throw new invalidparameterexception(result);
}
postcmmt.setcreatetime(new date());
return postcommentdao.create(postcmmt);
}
@override
public boolean delete(int id) {
return postcommentdao.delete(id);
}
@override
public boolean update(postcomment postcmmt) {
string result=verifymodel(postcmmt,false);
if(!result.isempty()) {
throw new invalidparameterexception(result);
}
return postcommentdao.update(postcmmt);
}
private string verifymodel(postcomment postcmmt,boolean isnew) {
stringbuilder errmsgbuilder=new stringbuilder();
if(!isnew && postcmmt.getid()<=0) {
errmsgbuilder.append("id不能为空!");
}
if(postcmmt.getpostid()<=0) {
errmsgbuilder.append("文章id不能为空!");
}
if(postcmmt.getcontent().trim().isempty()) {
errmsgbuilder.append("内容不能为空!");
}
if(postcmmt.getcreateby().trim().isempty()) {
errmsgbuilder.append("回复者不能为空!");
}
return errmsgbuilder.tostring();
}
}
//userserviceimpl.java
package cn.zuowenjun.java.mvc.service.impl;
import cn.zuowenjun.java.mvc.service.userservice;
import java.util.*;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpsession;
import org.springframework.stereotype.service;
import org.springframework.web.context.request.*;
@service
public class userserviceimpl implements userservice {
@override
public string login(string uid, string pwd) {
if(uid.isempty() || pwd.isempty()) {
return "用户名与密码都不能为空!";
}
resourcebundle userres= resourcebundle.getbundle("user");
string configuid= userres.getstring("user.userid");
string configpwd=userres.getstring("user.password");
if(configuid.equals(uid) && configpwd.equals(pwd)) {
string configuname=userres.getstring("user.username");
httpsession session= getrequest().getsession();
session.setattribute("loginuid", uid);
session.setattribute("loginuname",configuname);
return null;
}else{
return "用户名或密码不正确!";
}
}
@override
public string logout() {
try {
getrequest().getsession().removeattribute("loginuid");
return null;
}catch(exception ex) {
return ex.getmessage();
}
}
@override
public string getloginusername() {
object loginunameobj= getrequest().getsession().getattribute("loginuname");
if(loginunameobj==null) {
return null;
}else{
return (string)loginunameobj;
}
}
private httpservletrequest getrequest() {
httpservletrequest request= ((servletrequestattributes)requestcontextholder.getrequestattributes()).getrequest();
return request;
}
}
注意:登录服务中我使用了user.properties属性配置文件,username中的中文自动转码了。
user.userid=admin
user.password=www.zuowenjun.cn.java
user.username=\u68a6\u5728\u65c5\u9014
如上代码所示,所有的service实现类都标注了@service注解,之前的dao实现类也都标注了@repository,目的是为了实现spring容器的自动扫描并注册到ioc容器中,正如我在上面讲到的springmvc-servlet.xml配置文件中说的一样;另外service实现类除了调用dao完成数据的操作,另外还有业务数据的校验,比如代码中的:verifymodel方法等,当然实际的大型项目中可能业务逻辑复杂得多,模型验证也可以通过注解来实现,与c#中在system.componentmodel.dataannotations下的相关特性类似,大家有兴趣可以查阅相关资料;
2.6设计controller及view:
2.6.1创建一个blogcontroller类,用于处理管理博文(查看、发表、修改、删除)、评论(发表)的相关action,如下:
package cn.zuowenjun.java.mvc.controller;
import java.io.ioexception;
import java.security.invalidparameterexception;
import java.text.*;
import java.util.*;
import javax.servlet.http.httpservletresponse;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.beans.propertyeditors.customdateeditor;
import org.springframework.stereotype.controller;
import org.springframework.ui.model;
import org.springframework.ui.modelmap;
import org.springframework.web.bind.webdatabinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.webrequest;
import org.springframework.web.servlet.modelandview;
import org.springframework.web.servlet.mvc.support.redirectattributes;
import cn.zuowenjun.java.mvc.model.post;
import cn.zuowenjun.java.mvc.model.postcomment;
import cn.zuowenjun.java.mvc.service.*;
/**
*
* @author zuowenjun.cn
*refer-mavendepconfig:https://blog.csdn.net/qq_29227939/article/details/52063869
*refer-el:http://www.cnblogs.com/dongfangshenhua/p/6731421.html
*/
@controller
@requestmapping("/blog")
public class blogcontroller {
@autowired
private postservice postservice;
@autowired
private postcommentservice postcommentservice;
@requestmapping()
public modelandview list() {
list<post> postlist= postservice.getall();
modelandview mv=new modelandview();
mv.addobject("posts",postlist);
mv.setviewname("bloglist");
return mv;
}
@requestmapping(path="/querylist",method=requestmethod.post)
public modelandview list(@requestparam(required=true) date frmdate,@requestparam(required=true) date todate,modelandview mv) {
list<post> postlist=postservice.getlist(frmdate, todate);
mv.setviewname("bloglist");
mv.addobject("posts",postlist);
return mv;
}
@requestmapping("/post/{postid}")
public string detail(@pathvariable string postid,modelmap model) {
int pid=integer.parseint(postid);
model.put("post", postservice.get(pid));
model.put("comments", postcommentservice.getlist(pid));
return "blogdetail";
}
@requestmapping(path="/savecomment",method=requestmethod.post)
public string savecomment(@modelattribute() postcomment postcomment,redirectattributes redirectattr) {
string resultmsg="评论保存成功";
if(!postcommentservice.create(postcomment)) {
resultmsg="评论保存失败,请稍后重试";
}
redirectattr.addflashattribute("msg", resultmsg);
return "redirect:/blog/post/" + postcomment.getpostid();
}
@requestmapping(path="/editpost/{postid}",method=requestmethod.get)
public modelandview editpost(@pathvariable(required=true) int postid) {
modelandview mv=new modelandview();
post post=null;
post=postservice.get(postid);
if(post==null) {
throw new invalidparameterexception("无效的postid");
}
mv.addobject("post", post);
mv.setviewname("blogedit");
return mv;
}
@requestmapping("/editpost")
public string createpost(map<string,object> viewdatamap) {
post post=new post();
viewdatamap.put("post", post);
return "blogedit";
}
@requestmapping(path="/editpost",method=requestmethod.post)
public string updatepost(@modelattribute("post") post post,@requestparam("doaction") string action,model model,
httpservletresponse reponse) throws ioexception {
string result="保存成功!";
if(action.equals("delete")) { //删除操作
if(!postservice.delete(post.getid())){
result="删除失败,请重试!";
}else {
string jsresult="<script>alert('删除成功!');self.close();</script>";
reponse.setcontenttype("text/html;charset=utf-8");
reponse.getwriter().append(jsresult);
return null;
}
}
else { //编辑操作
if(post.getid()<=0) { //新增博文逻辑
int postid= postservice.create(post);
if(postid>0) {
post.setid(postid);
}else {
result="保存失败,请重试!";
}
}else if(!postservice.update(post)) { //更新博文逻辑
result="保存失败,请重试!";
}
}
model.addattribute("result", result);
return "blogedit";
}
@initbinder
public void initbinder(webdatabinder binder, webrequest request) {
//转换日期
dateformat dateformat=new simpledateformat("yyyy-mm-dd");
binder.registercustomeditor(date.class, new customdateeditor(dateformat, true));// customdateeditor为自定义日期编辑器
}
}
创建accountcontroller类,用于提供登录、登出的途径,代码如下:
package cn.zuowenjun.java.mvc.controller;
import java.io.ioexception;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.controller;
import org.springframework.ui.model;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.bind.annotation.requestparam;
import org.springframework.web.servlet.modelandview;
import cn.zuowenjun.java.mvc.service.userservice;
/**
* seeparambind:http://www.cnblogs.com/xiaoxi/p/5695783.html
*
*/
@controller
@requestmapping("/account")
public class accountcontroller {
@autowired
private userservice userservice;
@requestmapping("/signin")
public string signin() {
return "signin";
}
@requestmapping(path="/signin",method=requestmethod.post)
public modelandview signin(@requestparam(required=true) string uid,@requestparam(required=true) string pwd) {
string loginresult=userservice.login(uid, pwd);
modelandview mv= new modelandview();
if(loginresult==null || loginresult.isempty()) {//登录成功跳转
mv.setviewname("redirect:/blog");
}
else {
mv.setviewname("signin");
mv.addobject("message",loginresult==null || loginresult.isempty()?"登录成功":"登录失败:" + loginresult);
}
return mv;
}
@requestmapping("/signout")
public void signout(httpservletrequest request, httpservletresponse response) throws ioexception {
userservice.logout();
response.sendredirect(request.getcontextpath() + "/account/signin");
}
}
温馨提示:由于是demo演示,故我在controller中尽可能的使用不同的方式来完成各种逻辑,以便大家看到应用的效果,实际项目中不会是这样的。
关于controller类涉及如下知识点说明:
a.命名规范,统一使用资源名(一般是名词)+controller结尾,如示例:blogcontroller,accountcontroller,尽量符合rest的风格,同时类上标注:@controller,以便告诉spring容器这是一个controller的bean;
b.@requestmapping:指定映射的请求路径,与asp.net mvc的route特性有异由同工之效,详细用法可参考: 、
c.action获取请求参数(按照asp.net mvc的说法就是:model binding),详细用法可参考:
d.action指定视图以及为view指定model数据(传值给view):可以使用modelandview、model、modelmap、map<string,object>、@modelattribute(作用于action上)、httpservletrequest(其实前面的几种方法底层最终都是通过httpservletrequest.setattribute来实现的),注意除了可以直接返回modelandview,因为它包含了setview的方法,其余的都应该返回string的viewname,可以对照上面的示例代码看一下,也可参见:
e:controller间或action间的跳转,使用:redirect:action路径,action传参多种方法详见:,,我主要想特别说明的是redirectattributes,它不用url传参,而是采用一次性session的模式,类似asp.net mvc中的tempdata
2.6.2 创建相关ui视图页面(根据internalresourceviewresolver配置的视图解析位置及后缀名(/web-inf/jsp/、.jsp)在/web-inf/jsp/创建相关的jsp文件),分别有:signin.jsp(登录)、bloglist.jsp(博客列表主页)、blogdetail.jsp(博文详情,评论)、blogedit.jsp(博文编辑,新增、修改、删除),具体代码如下:
登录:
<%@ page language="java" contenttype="text/html; charset=utf-8"
pageencoding="utf-8" iselignored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>梦在旅途演示博客-登录</title>
<style type="text/css">
#loginbox{
width:300px;
margin:100px auto 0 auto;
padding:50px;
border:5px groove gray;
}
#loginbox div{
margin:20px auto;
}
.txtcenter{
text-align:center;
}
</style>
</head>
<body>
<form method="post">
<div id="loginbox">
<div><h3>欢迎,请登录!</h3></div>
<div>用户id:<input type="text" id="txtuid" name="uid" /></div>
<div>密 码:<input type="password" id="txtpwd" name="pwd" /></div>
<div>
<input type="submit" id="btnsubmit" value="登 录" />
<input type="reset" id="btnreset" value="重置" />
</div>
</div>
</form>
<div class="txtcenter">
<c:if test="${message!=null}">
<p>${message}</p>
</c:if>
</div>
<p class="txtcenter">copyright 2018 zuowj.cnblogs.com and zuowenjun.cn demo.</p>
</body>
</html>
博客列表主页、博文详情,评论:
<!-- bloglist.jsp -->
<%@ page language="java" contenttype="text/html; charset=utf-8"
pageencoding="utf-8" iselignored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>博客列表</title>
</head>
<body>
<div style="text-align: right;">
<span>[ ${sessionscope.loginuid }(${sessionscope.loginuname }),
<a href="${pagecontext.request.contextpath}/account/signout">[退出]</a> ]</span>
<a href="${pagecontext.request.contextpath}/blog/editpost" target="_blank">[ +发表博文 ]</a>
</div>
<div>
<form method="post" action="${pagecontext.request.contextpath }/blog/querylist">
<fieldset>
<legend>范围查询:</legend>
<span>开始时间:</span> <input type="text" name="frmdate"
value="${param.frmdate }"
placeholder="yyyy-mm-dd" />
<span>结束时间:</span> <input type="text"
name="todate" placeholder="yyyy-mm-dd"
value="${param.todate}" />
<button id="btnquery">查询</button>
</fieldset>
</form>
</div>
<c:choose>
<c:when test="${posts!=null && posts.size()>0}">
<c:foreach items="${posts}" var="item">
<div>
<h2>
<a href="${pagecontext.request.contextpath}/blog/post/${item.id }"
target="_blank">${item.title}</a>
<a href="${pagecontext.request.contextpath}/blog/editpost/${item.id }"
target="_blank" style="color:red;font-size:16px;">[修改]</a>
</h2>
<h4>
作者:${item.author },时间:
<fmt:formatdate value="${item.createtime}" pattern="yyyy-mm-dd" />
</h4>
<p>${item.content }</p>
</div>
<hr />
</c:foreach>
</c:when>
<c:otherwise>
<p style="color:red;">没有任何博客文章记录!</p>
</c:otherwise>
</c:choose>
</body>
</html>
<!-- blogedit.jsp -->
<%@ page language="java" contenttype="text/html; charset=utf-8"
pageencoding="utf-8" iselignored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>博文详情:${post.title }</title>
</head>
<body>
<div>
<h2>
${post.title}
</h2>
<h4>
作者:${post.author },
时间:<fmt:formatdate value="${post.createtime}" pattern="yyyy-mm-dd" />
</h4>
<p>${post.content }</p>
</div>
<hr/>
<div>
<c:choose>
<c:when test="${comments!=null && fn:length(comments)>0 }">
<c:foreach items="${ comments}" var="item">
<div style="margin:10px auto;">
<div style="border-bottom:solid 1px gray;margin-bottom:5px;">
${item.createby } 回复于:<fmt:formatdate value="${item.createtime}" pattern="yyyy-mm-dd hh:mm" />
</div>
<div>
${item.content }
</div>
</div>
</c:foreach>
</c:when>
<c:otherwise>
<p>暂无相关评论!</p>
</c:otherwise>
</c:choose>
<div>
<form method="post" action="../savecomment">
<h3>发表新评论:</h3>
<p>评论人:<input type="text" id="createby" name="createby" /></p>
<p>评论内容:</p>
<p><textarea rows="5" style="width:100%" id="content" name="content"></textarea>
</p>
<p>
<button id="btnreply">提交评论</button>
<input type="hidden" name="postid" value="${post.id }" />
</p>
</form>
<div>
<c:if test="${msg!=null}">
<p>提交评论结果:${msg}</p>
</c:if>
</div>
</div>
</div>
</body>
</html>
博文编辑:
<%@ page language="java" contenttype="text/html; charset=utf-8"
pageencoding="utf-8" iselignored="false" import="cn.zuowenjun.java.mvc.model.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>
<%-- ${post.id>0?"编辑"+ post.title:"新增博文" } --%>
<%
post post=(post)request.getattribute("post");
if(post==null){
out.print("post is null!");
return;
}
if(post.getid()>0){
out.print("编辑"+ post.gettitle());
}else{
out.print("新增博文");
}
%>
</title>
</head>
<body>
<form:form modelattribute="post" method="post" id="mainform"
action="${pagecontext.request.contextpath }/blog/editpost">
<div>文章标题:</div>
<div>
<form:input path="title" />
</div>
<div>作者:</div>
<div>
<form:input path="author" />
</div>
<div>文章内容:</div>
<div>
<form:textarea path="content" rows="10" style="width:100%;" />
</div>
<div>
<button type="button" id="btnsave" data-action="update" onclick="javascript:dosubmit(this);">保存</button>
<c:if test="${post.id>0 }">
<button type="button" id="btndelete" data-action="delete" style="color:red;" onclick="javascript:dosubmit(this);">删除</button>
</c:if>
<form:hidden path="id"/>
<input type="hidden" name="doaction" id="_doaction" />
</div>
</form:form>
<c:if test="${result!=null && fn:length(result)>0 }">
<p>操作结果:${result}</p>
</c:if>
<script type="text/javascript">
function dosubmit(btn){
if(!confirm("你确定要" + btn.innertext +"吗?")){
return false;
}
var actionval=btn.getattribute("data-action");
//alert(actionval);
document.getelementbyid("_doaction").setattribute("value",actionval);
document.getelementbyid("mainform").submit();
}
</script>
</body>
</html>
jsp视图涉及知识点说明:
a.jsp el表达式:简化java代码在前台的使用,具体用法详见:
b.jsp jstl(标准标签库):是一个jsp标签集合,它封装了jsp应用的通用核心功能,支持通用的、结构化的任务,比如迭代,条件判断,xml文档操作,国际化标签,sql标签等,具体用法详见:
个人认为如果需要用好jsp视图,就需要熟悉el及jstl这两个利器,如果是采用前后端分离那就另当别论;
2.7.为springmvc网站增加统一拦截器,实现统一的身份登录状态验证
2.7.1定义一个实现了handlerinterceptor接口的拦截器类:loginvalidationinterceptor,然后重写prehandle方法,在里面根据session来判断登录状态,若没有登录则跳转至登录页面,代码如下:
推荐阅读
-
JAVA WEB快速入门之从编写一个基于SpringBoot+Mybatis快速创建的REST API项目了解SpringBoot、SpringMVC REST API、Mybatis等相关知识
-
JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC
-
JAVA WEB快速入门之从编写一个基于SpringBoot+Mybatis快速创建的REST API项目了解SpringBoot、SpringMVC REST API、Mybatis等相关知识
-
JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC