与SSH(Struts/Spring/Hibernate/)同样,Spring+SpringMVC+MyBatis也有一个简称SSM,Spring实现业务对象管理,Spring MVC负责请求的转发和视图管理, MyBatis做为数据对象持久化引擎。这样搭配的优势是:轻量、自由度高、Spring与Spring MVC契合度更好。经过一个图书管理示例完成SSM框架的集成,能够将前面学习过的一些内容整合起来,使用到的知识包含:Spring、Spring MVC、MyBatis、JSR303校验、分页、文件上传、路径处理等。css
说明:本文只是为了讲解SSM在IDEA中的集成方法,若是您想了解更加详细的内容,请参考本人的另外一篇博客内容:Spring MVC 学习总结(六)——Spring+Spring MVC+MyBatis框架集成html
1.一、建立一个简单的项目,这里不使用模板。以下图所示:前端
1.二、填写好包名、项目名以下图所示:java
1.三、将项目转换成web项目jquery
转换成功:git
1.四、项目建立好后添加依赖包,方法有两种,一种直接使用maven,一种手动添加。程序员
使用maven:github
<?xml version="1.0" encoding="UTF-8"?> <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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tax</groupId> <artifactId>ssm</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.0.RELEASE</spring.version> </properties> <dependencies> <!--Spring框架核心库 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <!-- aspectJ AOP 织入器 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <!--mybatis-spring适配器 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!--Spring java数据库访问包,在本例中主要用于提供数据源 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!--log4j日志包 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.6.1</version> </dependency> <!-- mybatis ORM框架 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <!-- JUnit单元测试工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> <!--c3p0 链接池 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- JSTL --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Servlet核心包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <!--JSP --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> </dependencies> </project>
手动添加:web
找到要依赖的全部包面试
ok后关闭结构
无论使用何总方法,这里的Oracle数据库驱动都要手动添加。
选择项目右上角的“Edit Configurations”
添加tomcat
设置参数
设置应用上下文
添加输出目录
到这里项目就建立完成了。
打开Oracle数据库,建立一个表,这里以Book表为例,一个用于存放图书的表,共5个字段id表示编号,title表示图书名称,typename表示图书类型,price表示价格,state表示供出状态。SQL脚本以下:
--图书表 drop table book create table book ( id int not null primary key, --编号 title varchar2(128) not null unique, --书名 typename varchar2(128) not null, --类型 price numeric(10,2) default(0), --价格 state varchar2(10) default('未借出') --状态 ) -- 序列 drop sequence seq_book_id create sequence seq_book_id minvalue 9 maxvalue 9999999999999999999999999999 start with 69 increment by 1 cache 20; --添加图书 insert into book(id,title,Typename,Price,State) select 1,'零基础学Java(全彩版)','计算机',50.60,'未借出' from dual union select 2,'轻量级Java EE企业应用实战','软件工程',85.30,'未借出' from dual union select 3,'Java并发编程的艺术','软件工程',45.40,'未借出' from dual union select 4,'实战Java高并发程序设计','软件开发',48.70,'未借出' from dual union select 5,'Java程序员面试笔试宝典','神话',38.50,'已借出' from dual union select 6,'Java Web从入门到精通','计算机',71.00,'未借出' from dual union select 7,'Java编程思想(第4版)','计算机',70.10,'已借出' from dual union select 8,'深刻理解JAVA虚拟机','神话',65.00,'未借出' from dual union select 9,'从零开始写Java Web框架','计算机',63.20,'已借出' from dual --sql select id, title, typename, price, state from book where id=1; commit; select seq_book_id.nextval from dual; insert into book (id, title, typename, price, state) values (seq_book_id.nextval, 'testbook', '计算机', 99.8, '未借出'); update book set title=#{title},typename=#{typename},price=#{price},state=#{state} where id=#{id} delete from book where id=#{id}
表结构以下所示:
测试数据:
项目主要依赖的jar包有Spring核心包、Spring AOP包、Spring MVC包、MyBatis ORM包、MyBatis-Spring适配包、JSTL、JUnit、Log4j2等,具体的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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhangguo</groupId> <artifactId>SSMall</artifactId> <version>0.0.3</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.0.RELEASE</spring.version> </properties> <dependencies> <!--Spring框架核心库 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <!-- aspectJ AOP 织入器 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <!--mybatis-spring适配器 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!--Spring java数据库访问包,在本例中主要用于提供数据源 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!--Oracle数据库驱动 --> <dependency> <groupId>Oracle</groupId> <artifactId>Oracle-connector-java</artifactId> <version>5.1.38</version> </dependency> <!--log4j日志包 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.6.1</version> </dependency> <!-- mybatis ORM框架 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <!-- JUnit单元测试工具 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> <!--c3p0 链接池 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- JSTL --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Servlet核心包 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <!--JSP --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.5.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.2</version> </dependency> <!--JSR303 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.2.Final</version> </dependency> <!--文件上传 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <!-- FreeMarker --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency> </dependencies> </project>
若是是第一次依赖相关的包,则须要下载时间,请耐心等待,若是下载失败请手动下载(http://search.maven.org/)后复制到本地的资源库中。依赖后的项目结果以下:
为了实现与数据库中的books表进行关系映射新建一个Book图书类,具体代码以下:
package com.tax.model; /**图书 POJO*/ public class Book { private int id; //编号 private String title; //标题 private String typename; //类型 private Double price; //价格 private String state; //状态 //属性 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 getTypename() { return typename; } public void setTypename(String typename) { this.typename = typename; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public String getState() { return state; } public void setState(String state) { this.state = state; } }
完成后的界面
这个项目中咱们采用接口与注解结合的形式完成关系与对象间的映射,在接口中定义一些数据访问的方法,接口以下:
package com.tax.dao; import com.tax.model.Book; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; /**图书数据访问接口*/ public interface IBookDao { /**得到全部图书*/ @Select("select id, title, typename, price, state from book") public List<Book> getAllBooks(); /**得到单个图书对象经过编号*/ @Select("select id, title, typename, price, state from book where id=#{id}") public Book getBookById(int id); /**添加*/ @Insert("insert into Book(id, title, typename, price, state) values(seq_book_id.nextval,#{title}, #{typename}, #{price}, #{state})") public int add(Book entity); /**编辑*/ @Update("update Book set title=#{title}, typename=#{typename}, price=#{price}, state=#{state} where id=#{id}") public int edit(Book entity); /**删除*/ @Delete("delete from Book where id=#{id}") public int delete(int id); }
结果
为了保证数据访问正常,使用JUnit进行单元测试,在另外一个源代码目录src/test/java下添加一个名为TestBook的测试用例,编写完成的测试用例以下:
package com.tax.test; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import com.tax.entities.Book; import com.tax.mapper.BookDAO; import junit.framework.Assert; public class TestBook{ @Test public void getBookPagerTest() { int skip=4; int size=2; SqlSession session=MyBatisUtil.getSession(); try { BookDAO bookdao=session.getMapper(BookDAO.class); List<Book> Book=bookdao.getBookPager(skip, size); Assert.assertEquals(2, Book.size()); } finally { session.close(); } } @Test public void getBookByIdTest() { SqlSession session=MyBatisUtil.getSession(); try { BookDAO bookdao=session.getMapper(BookDAO.class); Book Book=bookdao.getBookById(1); Assert.assertEquals(1, Book.getId()); } finally { session.close(); } } @Test public void getBookCountTest() { SqlSession session=MyBatisUtil.getSession(); try { BookDAO bookdao=session.getMapper(BookDAO.class); Assert.assertEquals(9, bookdao.getBookCount()); } finally { session.close(); } } @Test public void insertTest() { SqlSession session=MyBatisUtil.getSession(); try { Book entity=new Book(); entity.setName("正宗无锡阳山水蜜桃新鲜水果水密桃12个6斤装江浙沪皖顺丰包邮"); entity.setPrice(108); entity.setPicture("nopic.jpg"); BookDAO bookdao=session.getMapper(BookDAO.class); Assert.assertEquals(1, bookdao.insert(entity)); } finally { session.close(); } } @Test public void deleteTest() { SqlSession session=MyBatisUtil.getSession(); try { BookDAO bookdao=session.getMapper(BookDAO.class); Assert.assertEquals(1, bookdao.delete(12)); } finally { session.close(); } } @Test public void update() { SqlSession session=MyBatisUtil.getSession(); try { BookDAO bookdao=session.getMapper(BookDAO.class); Book entity=bookdao.getBookById(12); entity.setName("正宗无锡阳山水蜜桃新鲜水果水密桃12个6斤装"); entity.setPrice(107); entity.setPicture("nopicture.jpg"); Assert.assertEquals(1, bookdao.update(entity)); } finally { session.close(); } } }
MyBatis访问数据库的工具类以下:
package com.tax.test; import java.io.InputStream; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public abstract class MyBatisUtil { public static SqlSessionFactory getSqlSessionFactory(){ // 得到环境配置文件流 InputStream config = MyBatisUtil.class.getClassLoader().getResourceAsStream("MyBatisCfg.xml"); // 建立sql会话工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config); return factory; } //得到会话 public static SqlSession getSession(){ return getSqlSessionFactory().openSession(true); } /** * 得到得sql会话 * @param isAutoCommit 是否自动提交,若是为false则须要sqlSession.commit();rollback(); * @return sql会话 */ public static SqlSession getSession(boolean isAutoCommit){ return getSqlSessionFactory().openSession(isAutoCommit); } }
MyBatis配置文件MyBatisCfg.xml以下所示:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties"></properties> <typeAliases> <package name="com.tax.entities" /> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${Oracle.driver}" /> <property name="url" value="${Oracle.url}" /> <property name="username" value="${Oracle.uid}" /> <property name="password" value="${Oracle.password}" /> </dataSource> </environment> </environments> <mappers> <mapper resource="com/zhangguo/ssmall/mapper/BookMapper.xml" /> </mappers> </configuration>
配置文件中使用到了db.properties属性文件,该文件用于存放数据库链接信息,文件内容以下:
#Oracle Oracle.driver=com.Oracle.jdbc.Driver Oracle.url=jdbc:Oracle://localhost:3306/db1 Oracle.uid=root Oracle.password=root
运行测试,一切正常,测试结果以下:
这里须要注意的是MyBatis配置文件的内容在后面与Spring整合后是会变化的,使用JUnit测试并未使用到Spring框架。
7.一、在源代码的根目录下修改db.properties文件,用于存放数据库链接信息,文件内容以下:
#Oracle Oracle.driver=oracle.jdbc.driver.OracleDriver Oracle.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl Oracle.uid=tax Oracle.password=orcl Oracle.acquireIncrement=5 Oracle.initialPoolSize=10 Oracle.minPoolSize=5 Oracle.maxPoolSize=20
7.二、在源代码的resource目录下新建 applicationContext.xml文件,用于整合MyBatis与Spring,很是关键,具体的内容以下:
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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!--1 引入属性文件,在配置中占位使用 --> <context:property-placeholder location="classpath*:db.properties" /> <!--2 配置C3P0数据源 --> <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!--驱动类名 --> <property name="driverClass" value="${Oracle.driver}" /> <!-- url --> <property name="jdbcUrl" value="${Oracle.url}" /> <!-- 用户名 --> <property name="user" value="${Oracle.uid}" /> <!-- 密码 --> <property name="password" value="${Oracle.password}" /> <!-- 当链接池中的链接耗尽的时候c3p0一次同时获取的链接数 --> <property name="acquireIncrement" value="${Oracle.acquireIncrement}"></property> <!-- 初始链接池大小 --> <property name="initialPoolSize" value="${Oracle.initialPoolSize}"></property> <!-- 链接池中链接最小个数 --> <property name="minPoolSize" value="${Oracle.minPoolSize}"></property> <!-- 链接池中链接最大个数 --> <property name="maxPoolSize" value="${Oracle.maxPoolSize}"></property> </bean> <!--3 会话工厂bean sqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 配置文件路径 --> <property name="configLocation" value="classpath:MyBatisCfg.xml"></property> <!-- 数据源 --> <property name="dataSource" ref="datasource"></property> <!-- sql映射文件路径 --> <!--<property name="mapperLocations" value="classpath*:com/tax/dao/*Mapper.xml"></property>--> </bean> <!--4 自动扫描对象关系映射 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--指定会话工厂,若是当前上下文中只定义了一个则该属性可省去 --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> <!-- 指定要自动扫描接口的基础包,实现接口 --> <property name="basePackage" value="com.tax.dao"></property> </bean> <!--5 声明式事务管理 --> <!--定义事物管理器,由spring管理事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"></property> </bean> <!--支持注解驱动的事务管理,指定事务管理器 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!--6 容器自动扫描IOC组件 --> <context:component-scan base-package="com.tax"></context:component-scan> <!--7 aspectj支持自动代理实现AOP功能 --> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> </beans>
从配置文件中能够看出第3点会话工厂配置中指定了MyBatis配置文件的位置与名称,其实也能够省去,在这里能够经过属性配置好。但我的认为当多个框架整合在一块儿时最后将配置文件分开,便于修改。修改后的MyBatisCfg.xml文件内容以下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties"></properties> <settings> <!--指定mybatis使用日志组件 --> <setting name="logImpl" value="LOG4J2" /> <!--开启全局的二级缓存 --> <setting name="cacheEnabled" value="false" /> <!--开启延时加载,若是有关联关系,则默认不会获取数据 延迟加载的全局开关。当开启时,全部关联对象都会延迟加载。 特定关联关系中可经过设置fetchType属性来覆盖该项的开关状态。 在association中指定fetchType="eager(当即)" 或者 lazy(延迟) 默认:false --> <setting name="lazyLoadingEnabled" value="true" /> <!--true时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载; false,每种属性将会按需加载。 默认为:true--> <setting name="aggressiveLazyLoading" value="false" /> </settings> <typeAliases> <package name="com.tax.model" /> </typeAliases> </configuration>
中间有一大段注释了,是由于MyBatis-Spring适配器已完成了这部份内容的工做,注释不删除的缘由是由于JUnit测试时还要使用,其它也可使用两个不一样的文件。
修改web.xml文件,注册加载Spring容器所需的监听器;注册Spring MVC前置控制器Servlet,中间还设置了Servlet3.0上传所需的参数;添加了一个全局的编码过滤器。
<?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"> <listener> <description>Spring容器加载监听器</description> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <description>设置Spring加载时的配置文件位置,默认位置在WEB-INF/lib目录下</description> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml</param-value> </context-param> <!--Spring MVC 前置Servlet,中心控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!--Spring MVC配置文件路径 --> <param-value>classpath*:springmvc-servlet.xml</param-value> </init-param> <!-- 启动动优先级,越小越早加载 --> <load-on-startup>1</load-on-startup> <!--Servlet3.0以上文件上传配置 --> <multipart-config> <!--上传文件的最大限制5MB --> <max-file-size>5242880</max-file-size> <!--请求的最大限制20MB --> <max-request-size>20971520</max-request-size> <!--当文件的大小超过临界值时将写入磁盘 --> <file-size-threshold>0</file-size-threshold> </multipart-config> </servlet> <!-- Servlet访问的路径映射,全部的访问都必须通过调度用的前置控制品 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!--编码过滤器 --> <filter> <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
在src/main/java源代码目录下添加applicationContext.xml文件,用于配置Spring,内容在上一节中已列出。
在src/main/java源代码目录下添加Spring MVC配置文件springmvc-servlet.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" 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-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd"> <!-- 自动扫描包,实现支持注解的IOC --> <context:component-scan base-package="com.tax" /> <!-- Spring MVC不处理静态资源 --> <mvc:default-servlet-handler /> <!-- 支持mvc注解驱动 --> <mvc:annotation-driven enable-matrix-variables="true" /> <!-- 配置映射媒体类型的策略 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> <property name="removeSemicolonContent" value="false" /> </bean> <!-- 内部视图解析器,JSP与JSTL模板 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <!--指定视图渲染类 --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <!--自动添加到路径中的前缀 --> <property name="prefix" value="/WEB-INF/views/" /> <!--自动添加到路径中的后缀 --> <property name="suffix" value=".jsp" /> <!--设置全部视图的内容类型,若是视图自己设置内容类型视图类能够忽略 --> <property name="contentType" value="text/html;charset=UTF-8" /> <!-- 优先级,越小越前 --> <property name="order" value="1" /> </bean> <!--文件上传解析器 --> <!--Spring MVC默认不能识别multipart格式的文件内容 --> <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"> </bean> </beans>
在包com.tax.service下添加IBookService.java文件,该文件是一个服务接口,内容以下:
package com.tax.service; import com.tax.model.Book; import java.util.List; /**图书业务接口*/ public interface IBookService { /**得到全部图书*/ public List<Book> getAllBooks(); /**得到单个图书对象经过编号*/ public Book getBookById(int id); /**添加*/ public int add(Book entity); /**编辑*/ public int edit(Book entity); /**删除*/ public int delete(int id); }
在包com.tax.service下添加类BookService.java,实现接口BookService,用于完成图书业务逻辑,因为是示例代码因此比较空;中间使用了两个注解一个是@Service,用于提供给须要服务的类自动装配,当Spring IOC容器启动时被扫描到该类型会自动添加实例到Spring容器中;另外一个注解是@Resource用于完成自动装配功能,在Spring容器中找到BookDAO类型的对象,代码以下:
package com.tax.service; import com.tax.dao.IBookDao; import com.tax.model.Book; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /**图书业务*/ @Service public class BookService implements IBookService { @Autowired IBookDao bookDao; public List<Book> getAllBooks() { return bookDao.getAllBooks(); } public Book getBookById(int id) { return bookDao.getBookById(id); } public int add(Book entity) { return bookDao.add(entity); } public int edit(Book entity) { return bookDao.edit(entity); } public int delete(int id) { return bookDao.delete(id); } }
定义BookController控制器,映射访问路径,须要使用到的图书服务使用自动装配完成,代码以下:
package com.tax.controller; import com.tax.service.IBookService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import javax.annotation.Resource; /**图书控制器*/ @Controller @RequestMapping("/Book") public class BookController { @Resource IBookService bookService; @RequestMapping("/list") public String list(Model model){ //得到全部的图书带到页面中 model.addAttribute("books",bookService.getAllBooks()); return "book/list"; } }
在views/jstl/Book目录下添加视图list.jsp页面,页面的内容以下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>图书管理</title> <meta charset="UTF-8"/> </head> <body> <h2>图书管理</h2> <table width="100%" id="tab" border="1"> <tr> <td>编号</td> <td>书名</td> <td>类型</td> <td>价格</td> <td>状态</td> <td>操做</td> </tr> <c:forEach var="book" items="${books}"> <tr> <td>${book.id}</td> <td>${book.title}</td> <td>${book.typename}</td> <td>${book.price}</td> <td>${book.state}</td> <td><a href="#">删除</a> | <a href="#">编辑</a></td> </tr> </c:forEach> </table> </body> </html>
测试运行结果:
修改业务层接口与实现:
IBookService:
package com.tax.service; import com.tax.model.Book; import java.util.List; /**图书业务接口*/ public interface IBookService { /**得到全部图书*/ public List<Book> getAllBooks(); /**得到单个图书对象经过编号*/ public Book getBookById(int id); /**添加*/ public int add(Book entity); /**编辑*/ public int edit(Book entity); /**删除*/ public int delete(int id); /**多删除*/ public int delete(int[] ids); }
BookService:
package com.tax.service; import com.tax.dao.IBookDao; import com.tax.model.Book; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /**图书业务*/ @Service public class BookService implements IBookService { @Autowired IBookDao bookDao; public List<Book> getAllBooks() { return bookDao.getAllBooks(); } public Book getBookById(int id) { return bookDao.getBookById(id); } public int add(Book entity) { return bookDao.add(entity); } public int edit(Book entity) { return bookDao.edit(entity); } public int delete(int id) { return bookDao.delete(id); } //多删除,重载 public int delete(int[] ids) { int rows=0; for (Integer id:ids) { //遍历全部的编号 rows+=delete(id); //调用单删除功能,累计删除行数 } return rows; } }
为了实现删除与多删除功能,修改控制器,增长2个action,delete请求处理方法用于删除单个记录,id是路径变量指定要删除的图书编号;pageNO是请求参数,保持状态的目的是为了删除后让页面继续停留在某一页,不过这里有问题的是当某一页的内容只有一条记录里就须要从新计算了;rediredtAttributes是为了保持重定向后的message值。
package com.tax.controller; import com.tax.service.IBookService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.annotation.Resource; /** * 图书控制器 */ @Controller @RequestMapping("/book") public class BookController { @Resource IBookService bookService; @RequestMapping("/list") public String list(Model model) { //得到全部的图书带到页面中 model.addAttribute("books", bookService.getAllBooks()); return "book/list"; //WEB-INF/views/book/list.jsp } //http://localhost:8080/ssm/book/del/5 @RequestMapping("/del/{id}") public String del(Model model, @PathVariable int id, RedirectAttributes flash) { //执行删除,若是影响行行数大于0,则成功 if (bookService.delete(id) > 0) { flash.addFlashAttribute("msg","删除成功!"); } else { flash.addFlashAttribute("msg","删除失败!"); } //重定向 return "redirect:/book/list"; } @RequestMapping("/dels") public String dels(Model model, @RequestParam(value = "id",required = false) int[] ids, RedirectAttributes flash) { //执行删除,若是影响行行数大于0,则成功 int rows=bookService.delete(ids); if ( rows> 0) { flash.addFlashAttribute("msg","删除成功"+rows+"行!"); } else { flash.addFlashAttribute("msg","删除失败!"); } //重定向 return "redirect:/book/list"; } }
为了配合删除,修改list.jsp页面,修改后的list.jsp页面以下所示:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>图书管理</title> <meta charset="UTF-8"/> </head> <body> <h2>图书管理</h2> <form action="dels" method="post"> <table width="100%" id="tab" border="1"> <tr> <td><input type="checkbox" id="chbAll" /></td> <td>编号</td> <td>书名</td> <td>类型</td> <td>价格</td> <td>状态</td> <td>操做</td> </tr> <c:forEach var="book" items="${books}"> <tr> <td><input name="id" type="checkbox" value="${book.id}"/></td> <td>${book.id}</td> <td>${book.title}</td> <td>${book.typename}</td> <td>${book.price}</td> <td>${book.state}</td> <td><a href="del/${book.id}" class="del">删除</a> | <a href="#">编辑</a></td> </tr> </c:forEach> </table> <p> <input type="submit" value="删除选择项" class="del"/> </p> </form> <p> ${msg} </p> <script src="<c:url value="/js/jquery-1.11.3.min.js"></c:url>"></script> <script> $(".del").click(function(){ return confirm("您肯定要删除吗?"); }); //全选与反选 $("#chbAll").change(function(){ $("input[name=id]").prop("checked",$(this).prop("checked")); }); var msg='${msg}'; if(msg){ alert(msg); } </script> </body> </html>
运行结果以下所示:
基中的多删除功能能够改进为一次性让数据库删除完成。
在控制器中添加2个action,一个是add用于完成添加页面展现,一个是addSave用于完成添加保存处理,代码以下:
//添加 @RequestMapping("/add") public String add(Model model) { return "book/add"; } //添加保存 @RequestMapping("/addSave") public String add(Model model, Book book, RedirectAttributes flash) { //执行删除,若是影响行行数大于0,则成功 if (bookService.add(book) > 0) { flash.addFlashAttribute("msg","添加成功!"); //重定向 return "redirect:/book/list"; } else { flash.addFlashAttribute("msg","添加失败!"); flash.addFlashAttribute("",book); return "redirect:/book/add"; } }
在views/jstl/book目录下新增长add.jsp页面,页面内容以下:
<%-- Created by IntelliJ IDEA. User: Administrator Date: 2018/3/20 Time: 10:31 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>添加图书</title> <meta charset="UTF-8"/> </head> <body> <h2>添加图书</h2> <form action="addSave" method="post"> <fieldset> <legend>图书详细</legend> <p> <label for="title">书名:</label> <input type="text" name="title" id="title" value="${book.title}"/> </p> <p> <label for="typename">类型:</label> <select name="typename" id="typename"> <option value="经济">经济</option> <option value="文学">文学</option> <option value="天文">天文</option> </select> </p> <p> <label for="price">价格:</label> <input type="text" name="price" id="price" value="${book.price}"/> </p> <p> <label for="state2">状态:</label> <input type="radio" name="state" id="state1" value="已借出"/>已借出 <input type="radio" name="state" id="state2" value="未借出"/>未借出 </p> <p> <input type="submit" value="提交"/> </p> </fieldset> </form> </body> </html>
首页添加新增按钮
<p> <input type="submit" value="删除选择项" class="del"/> <input type="button" value="添加" onclick="location.href='<c:url value="/book/add"></c:url>'" /> </p>
运行结果:
执行添加
与新增长相似,在控制器下新增两个action,一个用于展现编辑,有一个用于执行编辑后保存,代码以下所示:
//编辑 @RequestMapping("/edit") public String edit(Model model,int id) { //将要编辑的图书对象带入视图 model.addAttribute("book",bookService.getBookById(id)); return "book/edit"; } //编辑保存 @RequestMapping("/editSave") public String edit(Model model, Book book, RedirectAttributes flash) { //执行更新,若是影响行行数大于0,则成功 if (bookService.edit(book) > 0) { flash.addFlashAttribute("msg","更新成功!"); //重定向 return "redirect:/book/list"; } else { flash.addFlashAttribute("msg","更新失败!"); flash.addFlashAttribute("book",book); return "redirect:/book/edit"; } }
在views/jstl/book目录下新增长edit.jsp页面,页面内容以下:
<%-- Created by IntelliJ IDEA. User: Administrator Date: 2018/3/20 Time: 10:31 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>编辑图书</title> <meta charset="UTF-8"/> </head> <body> <h2>编辑图书</h2> <form action="editSave" method="post"> <fieldset> <legend>图书详细</legend> <p> <label for="title">书名:</label> <input type="text" name="title" id="title" value="${book.title}"/> </p> <p> <label for="typename">类型:</label> <select name="typename" id="typename"> <option value="计算机" ${book.typename=="计算机"?"selected='selected'":""}>计算机</option> <option value="软件工程" ${book.typename=="软件工程"?"selected='selected'":""}>软件工程</option> <option value="神话" ${book.typename=="神话"?"selected='selected'":""}>神话</option> <option value="软件开发" ${book.typename=="软件开发"?"selected='selected'":""}>软件开发</option> </select> </p> <p> <label for="price">价格:</label> <input type="text" name="price" id="price" value="${book.price}"/> </p> <p> <label for="state2">状态:</label> <input type="radio" name="state" id="state1" value="已借出" ${book.state=="已借出"?"checked='checked'":""}/>已借出 <input type="radio" name="state" id="state2" value="未借出" ${book.state=="未借出"?"checked='checked'":""}/>未借出 </p> <p> <input type="hidden" name="id" value="${book.id}" /> <input type="submit" value="保存"/> </p> </fieldset> </form> </body> </html>
首页编辑按钮:
<td><a href="del/${book.id}" class="del">删除</a> | <a href="<c:url value="/book/edit?id=${book.id}"></c:url>">编辑</a></td>
运行结果:
为了将MyBatis与Hibernate Validation的日志信息展现在控制中,须要添加log4j2的引用,这部份内容在pom.xml中已配置完成了,另外在项目的根目录下须要添加一个log4j2的配置文件log4j2.xml,内容以下:
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="off" monitorInterval="1800"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> </Appenders> <Loggers> <Root level="debug"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
在webapp目录下添加index.jsp,首页是这个程序的入口,只完成了转发功能,页面内容以下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:forward page="Book/list"></jsp:forward>
全部页面基本都引用了同一个样式表styles/main.css文件,文件内容以下:
@CHARSET "UTF-8"; * { margin: 0; padding: 0; font-family: microsoft yahei; font-size: 14px; } body { padding-top: 20px; } .main { width: 90%; margin: 0 auto; border: 1px solid #777; padding: 20px; border-radius: 5px; } .main .title { font-size: 20px; font-weight: normal; border-bottom: 1px solid #ccc; margin-bottom: 15px; padding-bottom: 5px; color: #006ac1; } .main .title span { display: inline-block; font-size: 20px; color: #fff; padding: 0 8px; background: orangered; border-radius: 5px; } a { color: #006ac1; text-decoration: none; } a:hover { color: orangered; } .tab td, .tab, .tab th { border: 1px solid #777; border-collapse: collapse; } .tab td, .tab th { line-height: 26px; height: 26px; padding-left: 5px; } .abtn { display: inline-block; height: 18px; line-height: 18px; background: #006ac1; color: #fff; padding: 0 5px; border-radius: 5px; } .btn { height: 18px; line-height: 18px; background: #006ac1; color: #fff; padding: 0 8px; border: 0; border-radius: 5px; } .abtn:hover, .btn:hover { background: orangered; color: #fff; } p { padding: 5px 0; } fieldset { border: 1px solid #ccc; padding: 5px 10px; } fieldset legend { margin-left: 10px; font-size: 16px; } a.out,input.out { height: 23px; line-height: 23px; } form{ margin:10px 0; } .error { color:red }
最终的控制器BookController.java文件内容以下:
package com.tax.controller; import com.tax.model.Book; import com.tax.service.IBookService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.annotation.Resource; /** * 图书控制器 */ @Controller @RequestMapping("/book") public class BookController { @Resource IBookService bookService; @RequestMapping("/list") public String list(Model model) { //得到全部的图书带到页面中 model.addAttribute("books", bookService.getAllBooks()); return "book/list"; //WEB-INF/views/book/list.jsp } //单删除 @RequestMapping("/del/{id}") public String del(Model model, @PathVariable int id, RedirectAttributes flash) { //执行删除,若是影响行行数大于0,则成功 if (bookService.delete(id) > 0) { flash.addFlashAttribute("msg","删除成功!"); } else { flash.addFlashAttribute("msg","删除失败!"); } //重定向 return "redirect:/book/list"; } //多删除 @RequestMapping("/dels") public String dels(Model model, @RequestParam(value = "id",required = false) int[] ids, RedirectAttributes flash) { //执行删除,若是影响行行数大于0,则成功 int rows=bookService.delete(ids); if ( rows> 0) { flash.addFlashAttribute("msg","删除成功"+rows+"行!"); } else { flash.addFlashAttribute("msg","删除失败!"); } //重定向 return "redirect:/book/list"; } //添加 @RequestMapping("/add") public String add(Model model) { return "book/add"; } //添加保存 @RequestMapping("/addSave") public String add(Model model, Book book, RedirectAttributes flash) { //执行保存,若是影响行行数大于0,则成功 if (bookService.add(book) > 0) { flash.addFlashAttribute("msg","添加成功!"); //重定向 return "redirect:/book/list"; } else { flash.addFlashAttribute("msg","添加失败!"); flash.addFlashAttribute("book",book); return "redirect:/book/add"; } } //编辑 @RequestMapping("/edit") public String edit(Model model,int id) { //将要编辑的图书对象带入视图 model.addAttribute("book",bookService.getBookById(id)); return "book/edit"; } //编辑保存 @RequestMapping("/editSave") public String edit(Model model, Book book, RedirectAttributes flash) { //执行更新,若是影响行行数大于0,则成功 if (bookService.edit(book) > 0) { flash.addFlashAttribute("msg","更新成功!"); //重定向 return "redirect:/book/list"; } else { flash.addFlashAttribute("msg","更新失败!"); flash.addFlashAttribute("book",book); return "redirect:/book/edit"; } } }
整合H-UI后的效果
通个该示例将前面几章的内容整合起来,巩固了前几章的内容;示例中还能够尝试使用FreeMarker视图;示例中没有前端验证都是后台验证,可使用jQuery扩展插件Validate实现前端校验;有些功能能够结合AJAX完成更加合理;路径是要很是当心的,后台重定向时,前台提交表单的路径,可使用base标签和c:url。内容比较简单,适合初学,只是但愿能起到抛砖引玉、以小见大的做用,谢谢阅读!
在Idea的spring工程里,常常会遇到Could not autowire. No beans of 'xxxx' type found的错误提示。但程序的编译和运行都是没有问题的,这个错误提示并不会产生影响。
下降Autowired检测的级别,将Severity的级别由以前的error改为warning或其它能够忽略的级别。