SSM综合练习
主要讲解maven工程搭建,以及基于oracle数据库的商品表信息,并完成SSM整合。
基于SSM整合基础上完成商品查询,要掌握主面页面main.jsp及商品显示页面product-list.jsp页面的创建。
进一步巩固SSM整合,并完成商品添加功能,要注意事务操作以及product-add.jsp页面生成。
订单的查询操作,它主要完成简单的多表查询操作,查询订单时,需要查询出与订单关联的其它表中信息,所以大 家一定要了解订单及其它表关联关系
订单分页查询,我们使用的是mybatis分页插件PageHelper,要掌握PageHelper的基本使用。
订单详情是用于查询某一个订单的信息,这个知识点主要考核学生对复杂的多表查询操作的掌握。
Spring Security是 Spring 项目组中用来提供安全认证服务的框架,它的使用很复杂,我们在课程中只介绍了 spring Security的基本操作,大家要掌握spring Security框架的配置及基本的认证与授权操作。
用户管理中我们会介绍基于spring Security的用户登录、退出操作。以及用户查询、添加、详情有等操作,这些功 能的练习是对前期SSM知识点的进一步巩固。
角色管理主要完成角色查询、角色添加
资源权限管理主要完成查询、添加操作,它的操作与角色管理类似,角色管理以及资源权限管理都是对权限管理的补充。
主要会讲解用户角色关联、角色权限关联,这两个操作是为了后续我们完成授权操作的基础,关于授权操作我们会 在服务器端及页面端分别讲解
AOP日志处理,我们使用spring AOP切面来完成系统级别的日志收集。
Oracle10g 账号:ssm 密码:ssm
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | productNum | varchar2(50) | 产品编号,唯一,不为空 |
3 | productName | varchar2(50) | 产品名称(路线名称) |
4 | cityName | varchar2(50) | 出发城市 |
5 | departureTime | timestamp | 出发时间 |
6 | productPrice | number | 产品价格 |
7 | productDesc | varchar(500) | 产品描述 |
8 | productStatus | int | 状态(0关闭1开启) |
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | ordertNum | varchar2(50) | 订单编号 不为空 唯一 |
3 | orderTime | timestamp | 下单时间 |
4 | peopleCount | int | 出行人数 |
5 | orderDesc | varchar2(500) | 订单描述(其他信息) |
6 | payType | int | 支付方式(0支付宝1微信2其他) |
7 | orderStatus | int | 订单状态(0未支付1已支付) |
8 | productId | int | 产品id外键 |
9 | memberid | int | 会员(联系人)id外键 |
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | name | varchar2(50) | 名称 |
3 | nickName | varchar2(50) | 呢称 |
4 | phoneNum | varchar2(50) | 电话号码 |
5 | varchar2(50) | 邮箱 |
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | name | varchar2(20) | 姓名 |
3 | sex | varchar2(20) | 性别 |
4 | phoneNum | varchar2(20) | 电话号码 |
5 | credentialsNum | int | 证件类型0身份证 1护照 2军官证 |
6 | credentialsNum | varchar(50) | 证件号码 |
7 | travellerType | int | 旅客类型(人群)0成人 1儿童 |
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | varchar2(20) | 非空,唯一 | |
3 | username | varchar2(50) | 用户名 |
4 | password | varchar2(20) | 密码(加密) |
5 | phoneNum | varchar2(20) | 电话 |
6 | status | int | 状态(0 未开启 1开启) |
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(50) | 无意义,主键uuid |
2 | roleName | varchar2(50) | 角色名 |
3 | roleDesc | varchar2(500) | 角色描述 |
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(50) | 无意义,主键uuid |
2 | permissionName | varchar2(50) | 权限名 |
3 | url | varchar2(500) | 资源路径 |
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(50) | 无意义,主键uuid |
2 | visitTime | timestamp | 访问时间 |
3 | username | varchar2(500) | 操作者用户名 |
4 | ip | varchar(50) | 访问ip |
5 | url | varchar2(20) | 访问资源url |
6 | executionTime | int | 执行时长 |
7 | method | varchar(20) | 访问方法 |
数据库我们使用Oracle Oracle 为每个项目创建单独user,oracle数据表存放在表空间下,每个用户有独立表空间
创建用户及密码:
语法[创建用户]:
create user 用户名 identified by 口令[即密码];
例子:
create user test identified by test;
授权
语法:
grant connect, resource to 用户名;
例子:
grant connect, resource to test;
PL/SQL Developer是一个集成开发环境,专门面向Oracle数据库存储程序单元的开发PL/SQL Developer侧重于易 用性、代码品质和生产力,充分发挥Oracle应用程序开发过程中的主要优势。
a) 创建用户
b) 授权
对象权限是指针对于某一张表的操作权限,系统权限是指对表的CRUD操作权限, 角色权限是系统权限的集合,我 们设置 时,一般是设置角色权限,设置resource与connect
产品表信息描述
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | productNum | varchar2(50) | 产品编号,唯一,不为空 |
3 | productName | varchar2(50) | 产品名称(路线名称) |
4 | cityName | varchar2(50) | 出发城市 |
5 | departureTime | timestamp | 出发时间 |
6 | productPrice | number | 产品价格 |
7 | productDesc | varchar(500) | 产品描述 |
8 | productStatus | int | 状态(0关闭1开启) |
创建表sql
CREATE TABLE product(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
productNum VARCHAR2(50) NOT NULL,
productName VARCHAR2(50),
cityName VARCHAR2(50),
DepartureTime timestamp,
productPrice Number,
productDesc VARCHAR2(500),
productStatus INT,
CONSTRAINT product UNIQUE (id, productNum)
)
insert into PRODUCT (id, productnum, productname, cityname, departuretime, productprice, productdesc,productstatus)
values ('676C5BD1D35E429A8C2E114939C5685A', 'itcast-002', '北京三日游', '北京', to_timestamp('1010-2018 10:10:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 1200, '不错的旅行', 1);
insert into PRODUCT (id, productnum, productname, cityname, departuretime, productprice, productdesc, productstatus)
values ('12B7ABF2A4C544568B0A7C69F36BF8B7', 'itcast-003', '上海五日游', '上海', to_timestamp('2504-2018 14:30:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 1800, '魔都我来了', 0);
insert into PRODUCT (id, productnum, productname, cityname, departuretime, productprice, productdesc, productstatus)
values ('9F71F01CB448476DAFB309AA6DF9497F', 'itcast-001', '北京三日游', '北京', to_timestamp('1010-2018 10:10:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 1200, '不错的旅行', 1);
pjy_ssm_domain
pjy_ssm_service
pjy_ssm_dao
pjy_ssm_utils
其中创建itcast-ssm-web 时注意我们选择一个web工程
在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.0modelVersion>
<groupId>com.pjy.ssmgroupId>
<artifactId>pjy_ssmartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<properties>
<spring.version>5.0.2.RELEASEspring.version>
<slf4j.version>1.6.6slf4j.version>
<log4j.version>1.2.12log4j.version>
<oracle.version>10.2.0.1.0oracle.version>
<mybatis.version>3.4.5mybatis.version>
<spring.security.version>5.0.1.RELEASEspring.security.version>
properties>
<dependencies>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.6.8version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>jsp-apiartifactId>
<version>2.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>${slf4j.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>${mybatis.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.0version>
dependency>
<dependency>
<groupId>c3p0groupId>
<artifactId>c3p0artifactId>
<version>0.9.1.2version>
<type>jartype>
<scope>compilescope>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.1.2version>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-webartifactId>
<version>${spring.security.version}version>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-configartifactId>
<version>${spring.security.version}version>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-coreartifactId>
<version>${spring.security.version}version>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-taglibsartifactId>
<version>${spring.security.version}version>
dependency>
<dependency>
<groupId>com.oraclegroupId>
<artifactId>ojdbc14artifactId>
<version>${oracle.version}version>
dependency>
<dependency>
<groupId>javax.annotationgroupId>
<artifactId>jsr250-apiartifactId>
<version>1.0version>
dependency>
dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.2version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
<showWarnings>trueshowWarnings>
configuration>
plugin>
plugins>
pluginManagement>
build>
<modules>
<module>pjy_ssm_daomodule>
<module>pjy_ssm_servicemodule>
<module>pjy_ssm_domainmodule>
<module>pjy_ssm_utilsmodule>
<module>pjy_ssm_webmodule>
modules>
project>
初始化maven-webapp缓慢,可以在创建maven时,使用标签:
archetypeCatalog internal
导入pom.xml坐标,出现Oracle14.jar包如法加载的情况,可以自行打入maven本地仓库。
在domain模块下创建商品实体类:
package com.pjy.ssm.domain;
import java.util.Date;
/*
* 商品实体类
* */
public class Product {
private String id; // 主键
private String productNum; // 编号 唯一
private String productName; // 名称
private String cityName; // 出发城市
private Date departureTime; // 出发时间
private String departureTimeStr; //格式化数据为字符串类型,方便前台展示
private double productPrice; // 产品价格
private String productDesc; // 产品描述
private Integer productStatus; // 状态 0 关闭 1 开启
private String productStatusStr;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getProductNum() {
return productNum;
}
public void setProductNum(String productNum) {
this.productNum = productNum;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public Date getDepartureTime() {
return departureTime;
}
public void setDepartureTime(Date departureTime) {
this.departureTime = departureTime;
}
public String getDepartureTimeStr() {
return departureTimeStr;
}
public void setDepartureTimeStr(String departureTimeStr) {
this.departureTimeStr = departureTimeStr;
}
public double getProductPrice() {
return productPrice;
}
public void setProductPrice(double productPrice) {
this.productPrice = productPrice;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public Integer getProductStatus() {
return productStatus;
}
public void setProductStatus(Integer productStatus) {
this.productStatus = productStatus;
}
public String getProductStatusStr() {
return productStatusStr;
}
public void setProductStatusStr(String productStatusStr) {
this.productStatusStr = productStatusStr;
}
@Override
public String toString() {
return "Product{" + "id='" + id + '\'' + ", productNum='" + productNum + '\'' + ", productName='" + productName + '\'' + ", cityName='" + cityName + '\'' + ", departureTime=" + departureTime + ", departureTimeStr='" + departureTimeStr + '\'' + ", productPrice=" + productPrice + ", productDesc='" + productDesc + '\'' + ", productStatus=" + productStatus + ", productStatusStr='" + productStatusStr + '\'' + '}';
}
}
package com.pjy.ssm.dao;
import com.pjy.ssm.domain.Product;
import java.util.List;
public interface IProductDao {
//查询所有的商品信息
public List<Product> findAll() throws Exception;
}
package com.pjy.ssm.service.impl;
import com.pjy.ssm.dao.IProductDao;
import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class productServiceImpl implements IProductService {
@Autowired
private IProductDao productDao;
@Override
public List<Product> findAll() throws Exception {
return productDao.findAll();
}
}
在ssm_web工程下创建java、resources资源文件夹,并制定相应的文件类型:
在resources下创建applicationContext.xml文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.pjy.ssm.service">
context:component-scan>
<context:component-scan base-package="com.pjy.ssm.dao">
context:component-scan>
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
bean>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.pjy.ssm.dao"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
beans>
在ssm_sercive中实现接口,并为接口实现impl类,通过注解配置:
package com.pjy.ssm.service.impl;
import com.pjy.ssm.dao.IProductDao;
import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class productServiceImpl implements IProductService {
@Autowired
private IProductDao productDao;
@Override
public List<Product> findAll() throws Exception {
return productDao.findAll();
}
}
在ssm_web下webapp-WEB-INF-web.xml中配置核心控制器:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListenerlistener-class>
listener>
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>*.dourl-pattern>
servlet-mapping>
<filter>
<filter-name>characterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>characterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<welcome-file-list>
<welcome-file>index.htmlwelcome-file>
<welcome-file>index.htmwelcome-file>
<welcome-file>index.jspwelcome-file>
<welcome-file>default.htmlwelcome-file>
<welcome-file>default.htmwelcome-file>
<welcome-file>default.jspwelcome-file>
welcome-file-list>
web-app>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:component-scan base-package="com.pjy.ssm.controller">
context:component-scan>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/" />
<property name="suffix" value=".jsp" />
bean>
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/img/" mapping="/img/**" />
<mvc:resources location="/js/" mapping="/js/**" />
<mvc:resources location="/plugins/" mapping="/plugins/**" />
<mvc:annotation-driven />
<aop:aspectj-autoproxy proxy-target-class="true"/>
beans>
在ssm_web-main-java下创建com.pjy.ssm.controller包,创建ProductController类:
package com.pjy.ssm.controller;
import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/product")
public class ProductController {
@Autowired
private IProductService productService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<Product> ps = productService.findAll();
mv.addObject("productList",ps);
mv.setViewName("product-list");
return mv;
}
}
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListenerlistener-class>
listener>
把 mybatis 配置文件(mybatis.xml)中内容配置到 spring 配置文件中 同时,把 mybatis 配置文件的内容清掉。
<configuration>
configuration>
注意: 理 由于我们使用的是代理 Dao 的模式,Dao 具体实现类由 MyBatis 使用代理方式创建,所以此时 mybatis 配置文件不能删。 当我们整合 spring 和 mybatis 时,mybatis 创建的 Mapper.xml 文件名必须和 Dao 接口 文件 名一致
在ssm_web-resources下创建db.properties资源文件,设置值:
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
jdbc.username=ssm
jdbc.password=ssm
在applicationContext.xml中配置连接池:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
bean>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.pjy.ssm.dao"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
使用AdminLTE开源框架,将HTML文件,转换为jsp文件,在webapp下引入资源文件,css、img、plugins并创建pages包存放页面信息。使用maven中的Tomcat7启动,这样可以查看项目在服务器上运行的状态,在启动前在pom.xml中配置(可以使用快捷键)
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<configuration>
<port>8888port>
configuration>
<version>2.2version>
plugin>
plugins>
在maven管理中清理工程,打包,启动Tomcat服务器:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
Hello World!
查询所有的商品信息
点击请求连接,触发controller控制器中的ProductController类,执行findAll()方法,方法中创建一个新的视图解析器,调用service层中ProductService实现类中的findAll()方法,在service实现中调用持久层findAll()方法,通过注解配置查询语句:
@Select("select * from product")
在将查询到的数据封装List中返回给业务层,业务层返回给controller中,在通过addObject()方法添加到视图解析器中,通过前端页面product-list中的items属性,遍历视图解析器中的元素
${product.id }
${product.productNum }
${product.productName }
${product.cityName }
${product.departureTimeStr }
${product.productPrice }
${product.productDesc }
${product.productStatusStr }
controller:
package com.pjy.ssm.controller;
import com.pjy.ssm.domain.Product;
import com.pjy.ssm.service.IProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/product")
public class ProductController {
@Autowired
private IProductService productService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<Product> ps = productService.findAll();
mv.addObject("productList",ps);
mv.setViewName("product-list");
return mv;
}
}
启动服务器:
http://localhost:8888/pjy_ssm_web 点击链接: [查询所有的商品信息],得到所有的商品信息
在web-pages下创建页面
在web-controller包下ProductController中创建save方法
/*
* 产品添加
* */
@RequestMapping("/save.do")
public void save(Product product) throws Exception {
productService.save(product);
}
跳转到service中,添加实现类,调用save方法:
@Override
public void save(Product product) throws Exception {
productDao.save(product);
}
跳转到Dao中,实现商品的添加:
package com.pjy.ssm.dao;
import com.pjy.ssm.domain.Product;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface IProductDao {
//查询所有的商品信息
@Select("select * from product")
public List<Product> findAll() throws Exception;
//添加商品
@Insert("insert into product(productNum,productName,cityName,departureTime,productPrice,productDesc,productStatus) values(#{productNum},#{productName},#{cityName},#{departureTime},#{productPrice},# {productDesc},#{productStatus})")
void save(Product product);
}
启动服务器发现出现400错误:
加入log4g到resources资源下,启动服务器,查看日志信息,检查错误来源:
查看日志,可以看到错误信息中显示,departureTime类型错误,要将页面传送的String类型转换为date类型,保存到数据库中,这是一个典型了SpringMVC中类型转换问题。
解决方法:
可以使用
1、局部解决方法:在product实体类中date属性上加入注解:
2、使用全局设置(暂未实现)
清空项目包,从新启动tomcat:
得到查询结果。
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | ordertNum | varchar2(50) | 订单编号 不为空 唯一 |
3 | orderTime | timestamp | 下单时间 |
4 | peopleCount | int | 出行人数 |
5 | orderDesc | varchar2(500) | 订单描述(其他信息) |
6 | payType | int | 支付方式(0支付宝1微信2其他) |
7 | orderStatus | int | 订单状态(0未支付1已支付) |
8 | productId | int | 产品id外键 |
9 | memberid | int | 会员(联系人)id外键 |
productId描述了订单与产品之间的关系。
memberid描述了订单与会员之间的关系。
创建表sql
CREATE TABLE orders(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
orderNum VARCHAR2(20) NOT NULL UNIQUE,
orderTime timestamp,
peopleCount INT,
orderDesc VARCHAR2(500),
payType INT,
orderStatus INT,
productId varchar2(32),
memberId varchar2(32),
FOREIGN KEY (productId)
REFERENCES product(id),
FOREIGN KEY (memberId) REFERENCES member(id)
);
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('0E7231DC797C486290E8713CA3C6ECCC', '12345', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '676C5BD1D35E429A8C2E114939C5685A', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('5DC6A48DD4E94592AE904930EA866AFA', '54321', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '676C5BD1D35E429A8C2E114939C5685A', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('2FF351C4AC744E2092DCF08CFD314420', '67890', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('A0657832D93E4B10AE88A2D4B70B1A28', '98765', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('E4DD4C45EED84870ABA83574A801083E', '11111', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('96CC8BD43C734CC2ACBFF09501B4DD5D', '22222', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '12B7ABF2A4C544568B0A7C69F36BF8B7', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('55F9AF582D5A4DB28FB4EC3199385762', '33333', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '9F71F01CB448476DAFB309AA6DF9497F', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('CA005CF1BE3C4EF68F88ABC7DF30E976', '44444', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '9F71F01CB448476DAFB309AA6DF9497F', 'E61D65F673D54F68B0861025C69773DB');
insert into ORDERS (id, ordernum, ordertime, peoplecount, orderdesc, paytype, orderstatus, productid, memberid)
values ('3081770BC3984EF092D9E99760FDABDE', '55555', to_timestamp('02-03-2018 12:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'), 2, '没什么', 0, 1, '9F71F01CB448476DAFB309AA6DF9497F', 'E61D65F673D54F68B0861025C69773DB');
实体类:
public class Orders {
private String id; private String orderNum; private Date orderTime;
private String orderTimeStr;
private int orderStatus;
private int peopleCount;
private Product product;
private List<Traveller> travellers;
private Member member;
private Integer payType;
private String payTypeStr;
private String orderDesc;
//省略getter/setter
}
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | name | varchar2(50) | 名称 |
3 | nickName | varchar2(50) | 呢称 |
4 | phoneNum | varchar2(50) | 电话号码 |
5 | varchar2(50) | 邮箱 |
订单与会员之间是多对一关系,我们在订单表中创建一个外键来进行关联。
CREATE TABLE member(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
NAME VARCHAR2(20),
nickname VARCHAR2(20),
phoneNum VARCHAR2(20),
email VARCHAR2(20)
)
insert into MEMBER (id, name, nickname, phonenum, email) values ('E61D65F673D54F68B0861025C69773DB', '张三', '小三', '18888888888', '[email protected]');
实体类:
public class Member {
private String id;
private String name;
private String nickname;
private String phoneNum;
private String email;
//省略getter/setter
}
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | name | varchar2(20) | 姓名 |
3 | sex | varchar2(20) | 性别 |
4 | phoneNum | varchar2(20) | 电话号码 |
5 | credentialsNum | int | 证件类型0身份证 1护照 2军官证 |
6 | credentialsNum | varchar(50) | 证件号码 |
7 | travellerType | int | 旅客类型(人群)0成人 1儿童 |
创建表sql
CREATE TABLE traveller(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
NAME VARCHAR2(20),
sex VARCHAR2(20),
phoneNum VARCHAR2(20),
credentialsType INT,
credentialsNum VARCHAR2(50),
travellerType INT
) ;
insert into TRAVELLER (id, name, sex, phonenum, credentialstype, credentialsnum, travellertype) values ('3FE27DF2A4E44A6DBC5D0FE4651D3D3E', '张龙', '男', '13333333333', 0, '123456789009876543', 0);
insert into TRAVELLER (id, name, sex, phonenum, credentialstype, credentialsnum, travellertype) values ('EE7A71FB6945483FBF91543DBE851960', '张小龙', '男', '15555555555', 0, '987654321123456789', 1);
实体类:
public class Traveller {
private String id;
private String name;
private String sex;
private String phoneNum;
private Integer credentialsType;
private String credentialsTypeStr;
private String credentialsNum;
private Integer travellerType;
private String travellerTypeStr;
//省略getter/setter
}
旅客与订单之间是多对多关系,所以我们需要一张中间表(order_traveller)来描述。
序号 | 字段名称 | 字段类型 | 字段描述 |
---|---|---|---|
1 | orderid | varchar2(32) | 订单id |
2 | travellerId | varchar2(32) | 旅客id |
创建sql
CREATE TABLE order_traveller(
orderId varchar2(32),
travellerId varchar2(32),
PRIMARY KEY (orderId,travellerId),
FOREIGN KEY (orderId) REFERENCES orders(id),
FOREIGN KEY (travellerId) REFERENCES traveller(id)
);
insert into ORDER_TRAVELLER (orderid, travellerid) values ('0E7231DC797C486290E8713CA3C6ECCC', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('2FF351C4AC744E2092DCF08CFD314420', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('3081770BC3984EF092D9E99760FDABDE', 'EE7A71FB6945483FBF91543DBE851960');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('55F9AF582D5A4DB28FB4EC3199385762', 'EE7A71FB6945483FBF91543DBE851960');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('5DC6A48DD4E94592AE904930EA866AFA', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('96CC8BD43C734CC2ACBFF09501B4DD5D', 'EE7A71FB6945483FBF91543DBE851960');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('A0657832D93E4B10AE88A2D4B70B1A28', '3FE27DF2A4E44A6DBC5D0FE4651D3D3E');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('CA005CF1BE3C4EF68F88ABC7DF30E976', 'EE7A71FB6945483FBF91543DBE851960');
insert into ORDER_TRAVELLER (orderid, travellerid) values ('E4DD4C45EED84870ABA83574A801083E', 'EE7A71FB6945483FBF91543DBE851960');
在ssm_web板块创建OrderController
package com.pjy.ssm.controller;
import com.pjy.ssm.domain.Orders;
import com.pjy.ssm.service.IOrdersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/orders")
public class OrdersController {
@Autowired
private IOrdersService ordersService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<Orders> ordersList = ordersService.findAll();
mv.addObject("ordersList",ordersList);
mv.setViewName("orders-list");
return mv;
}
}
创建service层接口IOrdersService添加实现类:
package com.pjy.ssm.service.impl;
import com.pjy.ssm.dao.IOrdersDao;
import com.pjy.ssm.domain.Orders;
import com.pjy.ssm.service.IOrdersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class OrdersServiceImpl implements IOrdersService {
@Autowired
private IOrdersDao ordersDao;
@Override
public List<Orders> findAll() throws Exception {
return ordersDao.findAll();
}
}
在service中调用Dao层IOrdersDao类中的方法:
package com.pjy.ssm.dao;
import com.pjy.ssm.domain.Orders;
import com.pjy.ssm.domain.Product;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface IOrdersDao {
/*
* 在查询中添加映射关系,在此实体类和数据库中的字段名相同,不用改别名
* 在查询中Product和Orders是一对多的关系(由于旅游产品的业务需求决定的,一个订单中只有一个旅游地址,一个旅游地址可以对应多个订单(一对多))
* id = true:设置Orders表中的主键
* 元素名称和属性名相同(实体类对应表结构)
* column = "productId":实体类product对应product实体类中的productId属性,
* one = @One:指定对应关系:调用IProductDao:中的findById查询方法
* 此处用的是注解形式,也可以配置xml文件
* */
@Select("select * from orders")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "orderNum", column = "orderNum"),
@Result(property = "orderTime", column = "orderTime"),
@Result(property = "orderStatus", column = "orderStatus"),
@Result(property = "peopleCount", column = "peopleCount"),
@Result(property = "payType", column = "payType"),
@Result(property = "payTypeStr", column = "payTypeStr"),
@Result(property = "orderDesc", column = "orderDesc"),
@Result(property = "product", column = "productId",javaType = Product.class, one = @One(select = "com.pjy.ssm.dao.IProductDao.findById")),
})
public List<Orders> findAll() throws Exception;
}
在Dao层中的IProduct接口中添加查询方法:
//根据id查询产品
@Select("select * from product where id=#{id}")
public Product findById(String id) throws Exception;
重新打包,启动服务器,查看状态,得到查询结果,如上图所示。
PageHelper是国内非常优秀的一款开源的mybatis分页插件,它支持基本主流与常用的数据库,例如mysql、 oracle、mariaDB、DB2、SQLite、Hsqldb等。
本项目在 github 的项目地址:https://github.com/pagehelper/Mybatis-PageHelper
本项目在 gitosc 的项目地址:http://git.oschina.net/free/Mybatis_PageHelper
1、集成
引入分页插件有下面2种方式,推荐使用 Maven 方式。
1.1、引入 Jar 包
你可以从下面的地址中下载最新版本的 jar 包
https://oss.sonatype.org/content/repositories/releases/com/github/pagehelper/pagehelper/
http://repo1.maven.org/maven2/com/github/pagehelper/pagehelper/
由于使用了sql 解析工具,你还需要下载 jsqlparser.jar:
http://repo1.maven.org/maven2/com/github/jsqlparser/jsqlparser/0.9.5/
1.2、使用 Maven
在 pom.xml 中添加如下依赖:
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>最新版本version>
dependency>
2、配置
特别注意,新版拦截器是 com.github.pagehelper.PageInterceptor 。
com.github.pagehelper.PageHelper 现 在是一个特殊的 dialect 实现类,是分页插件的默认实现类,提供了和以前相同的用法。
2.1、 在 MyBatis 配置 xml 中配置拦截器插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="param1" value="value1"/>
plugin>
plugins>
2.2、在 Spring 配置文件中配置拦截器插件
使用 spring 的属性配置方式,可以使用 plugins 属性像下面这样配置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<prop key="helperDialect">oracleprop>
<prop key="reasonable">trueprop>
props>
property>
bean>
array>
property>
bean>
2.3、 分页插件参数介绍
1、helperDialect :分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置 helperDialect 属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值: oracle , mysql , mariadb , sqlite , hsqldb , postgresql , db2 , sqlserver , informix , h2 , sqlserver2012 , derby
特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012 ,否则会使用 SqlServer2005 的 方式进行分页。 你也可以实现 AbstractHelperDialect ,然后配置该属性为实现类的全限定名称即可使用自定义的实现方 法。
2、 offsetAsPageNum :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分 页。
3、 rowBoundsWithCount :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置 为 true 时,使用 RowBounds 分页会进行 count 查询。
4、pageSizeZero :默认值为 false ,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
5、 reasonable :分页合理化参数,默认值为 false 。当该参数设置为 true 时, pageNum<=0 时会查询第一 页, pageNum>pages (超过总数时),会查询最后一页。默认 false 时,直接根据参数进行查询。
6、 params :为了支持 startPage(Object params) 方法,增加了该参数来配置参数映射,用于从对象中根据属 性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable ,不配置映射的用默认值, 默认 值为 pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero 。
7、supportMethodsArguments :支持通过 Mapper 接口参数来传递分页参数,默认值 false ,分页插件会从查 询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法 可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest 。
8、autoRuntimeDialect :默认值为 false 。设置为 true 时,允许在运行时根据多数据源自动识别对应方言 的分页 (不支持自动选择 sqlserver2012 ,只能使用 sqlserver ),用法和注意事项参考下面的场景五。
9、 closeConn :默认值为 true 。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类 型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认 true 关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。
2.4、基本使用
PageHelper的基本使用有6种,大家可以查看文档,最常用的有两种
1、RowBounds方式的调用(了解)
List<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));
使用这种调用方式时,你可以使用RowBounds参数进行分页,这种方式侵入性最小,我们可以看到,通过 RowBounds方式调用只是使用了这个参数,并没有增加其他任何内容。
分页插件检测到使用了RowBounds参数时,就会对该查询进行物理分页。 关于这种方式的调用,有两个特殊的参数是针对 RowBounds 的,你可以参看上面的分页插件参数介绍
注:不只有命名空间方式可以用RowBounds,使用接口的时候也可以增加RowBounds参数,例如:
//这种情况下也会进行物理分页查询
List<Country> selectAll(RowBounds rowBounds);
注意: 由于默认情况下的 RowBounds 无法获取查询总数,分页插件提供了一个继承自 RowBounds 的 PageRowBounds ,这个对象中增加了 total 属性,执行分页查询后,可以从该属性得到查询总数。
2、PageHelper.startPage 静态方法调用(重点)
这种方式是我们要掌握的 在你需要进行分页的 MyBatis 查询方法前调用PageHelper.startPage 静态方法即可,紧 跟在这个方法后的第一个MyBatis 查询方法会被进行分页。
//获取第1页,10条内容,默认查询总数
count PageHelper.startPage(1, 10);
//紧跟着的第一个select方法会被分页
List<Country> list = countryMapper.selectIf(1);
1、Controller
在OrdersController中的findAll()方法中,添加查询方法:
@RequestMapping("/findAll.do")
public ModelAndView findAll(@RequestParam(name = "page", required = true,defaultValue = "1") int page,@RequestParam(name = "size",required = true,defaultValue = "5") int size) throws Exception {
//创建视图模板
ModelAndView mv = new ModelAndView();
//在查询方法中添加分页参数
List<Orders> ordersList = ordersService.findAll(page, size);
//PageInfo--一个分页的JavaBean对象
PageInfo pageInfo = new PageInfo(ordersList);
mv.addObject("pageInfo",pageInfo);
mv.setViewName("orders-page-list");
return mv;
}
2、service
在OrdersService接口实现类中findAll方法上传入参数page、size,得到前端页面中传递的当前页数和分页数,
注意:PageHelper方法必须在调用Dao方法的上方,写到其他地方都没法正常使用:
@Override
public List<Orders> findAll(int page, int size) throws Exception {
//参数pageNum是页码值,参数pageSize代表是每页显示条数
PageHelper.startPage(page,size);
return ordersDao.findAll();
}
3、order-page-list.jsp界面:
在前端界面中注意Controller传递的list集合是pageInfo,所以在遍历时,要修改成对应的集合信息(items):
${orders.id }
${orders.orderNum }
${orders.product.productName }
${orders.product.productPrice }
${orders.orderTimeStr }
${orders.orderStatusStr }
在Controller中我们接受了PageInfo对象,通过调用封装的对应属性值,完成页面的跳转功能,在每页显示条数功能上用js实现,在applicationContext.xml中,配置了边界限制,所以当超过第一页和最后一页时,会默认回到当前页码:
清空项目,重启项目,点击基础数据-订单管理,查看是否正确
在order-page-list.jsp页面上对"详情"添加链接
<td class="text-center">
<button type="button" class="btn bg-olive btn-xs">订单</button>
<button type="button" class="btn bg-olive btn-xs" onclick="location.href='${pageContext.request.contextPath}/orders/findById.do?id=${orders.id}'">详情</button>
<button type="button" class="btn bg-olive btn-xs">编辑</button>
</td>
导入order-show.jsp页面到pages中
在OrdersController中添加方法findById,传入id参数:
@RequestMapping("/findById.do")
public ModelAndView findById(@RequestParam(name = "id", required = true) String ordersId) throws Exception {
ModelAndView mv = new ModelAndView();
Orders orders = ordersService.findById(ordersId);
mv.addObject("orders",orders);
mv.setViewName("orders-show");
return mv;
}
6.5.2 Service
在OrdersServiceIpml实现类中添加findById方法,传入ordersId:
@Override
public Orders findById(String ordersId) throws Exception {
return ordersDao.findById(ordersId);
}
6.5.3 Dao
在IOrdersDao中添加findById在查询订单orders中包含member(会员)信息(多对一)、product(产品)信息(多对一)、travellers(游客)信息(多对多)。
/*
* 根据id查询订单详情
* 在查询过程中有多表操作
* */
@Select("select * from orders where id = #{ordersId}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "orderNum", column = "orderNum"),
@Result(property = "orderTime", column = "orderTime"),
@Result(property = "orderStatus", column = "orderStatus"),
@Result(property = "peopleCount", column = "peopleCount"),
@Result(property = "payType", column = "payType"),
@Result(property = "payTypeStr", column = "payTypeStr"),
@Result(property = "orderDesc", column = "orderDesc"),
@Result(property = "product", column = "productId",javaType = Product.class, one = @One(select = "com.pjy.ssm.dao.IProductDao.findById")),
@Result(property = "member", column = "memberId",javaType = Member.class, one = @One(select = "com.pjy.ssm.dao.IMemberDao.findById")),
@Result(property = "travellers",column = "id",javaType = java.util.List.class, many = @Many(select = "com.pjy.ssm.dao.ITravellerDao.findByOrdersId")),
})
Orders findById(String ordersId) throws Exception;
在dao层,分别创建IMemberDao、ITravellerDao接口,的查询方法,分别对应Result中的查询。
IMemberDao的findById方法
@Select("select * from member where id=#{id}")
Member findById(String id) throws Exception;
ITravellerDao.findByOrdersId方法
@Select("select * from traveller where id in (select travellerId from order_traveller where orderId=#{ordersId})")
List<Traveller> findByOrdersId(String ordersId) throws Exception;
重启服务器,访问订单详情页面:
1、用户表信息描述users
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(32) | 无意义,主键uuid |
2 | varchar2(20) | 非空,唯一 | |
3 | username | varchar2(50) | 用户名 |
4 | password | varchar2(20) | 密码(加密) |
5 | phoneNum | varchar2(20) | 电话 |
6 | status | int | 状态(0 未开启 1开启) |
2、sql语句
CREATE TABLE users(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
email VARCHAR2(50) UNIQUE NOT NULL,
username VARCHAR2(50),
PASSWORD VARCHAR2(50),
phoneNum VARCHAR2(20),
STATUS INT
)
3、实体类
public class UserInfo {
private String id;
private String username;
private String email;
private String password;
private String phoneNum;
private int status;
private String statusStr;
private List<Role> roles;
}
1、 角色表信息描述role
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(50) | 无意义,主键uuid |
2 | roleName | varchar2(50) | 角色名 |
3 | roleDesc | varchar2(500) | 角色描述 |
2、SQL语句
CREATE TABLE role(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
roleName VARCHAR2(50) ,
roleDesc VARCHAR2(50)
)
3、实体类
public class Role {
private String id;
private String roleName;
private String roleDesc;
private List<Permission> permissions;
private List<User> users;
}
4、用户与角色关联关系
用户与角色之间是多对多关系,我们通过user_role表来描述其关联,在实体类中User中存在List,在Role中有List. 而角色与权限之间也存在关系,我们会在后面介绍
CREATE TABLE users_role(
userId varchar2(32),
roleId varchar2(32),
PRIMARY KEY(userId,roleId),
FOREIGN KEY (userId) REFERENCES users(id),
FOREIGN KEY (roleId) REFERENCES role(id)
)
1、权限资源表描述permission
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(50) | 无意义,主键uuid |
2 | permissionName | varchar2(50) | 权限名 |
3 | url | varchar2(500) | 资源路径 |
2、SQL语句
CREATE TABLE permission(
id varchar2(32) default SYS_GUID() PRIMARY KEY,
permissionName VARCHAR2(50) ,
url VARCHAR2(50)
)
3、实体类
public class Permission {
private String id;
private String permissionName;
private String url;
private List<Role> roles;
}
4、权限资源与角色关联关系
权限资源与角色是多对多关系,我们使用role_permission表来描述。在实体类Permission中存在List,在Role类中 有List
CREATE TABLE role_permission(
permissionId varchar2(32),
roleId varchar2(32),
PRIMARY KEY(permissionId,roleId),
FOREIGN KEY (permissionId) REFERENCES permission(id),
FOREIGN KEY (roleId) REFERENCES role(id)
)
1、Spring Security介绍
Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。
(https://projects.spring.io/spring-security/) Spring Security 为基于J2EE企业应用软件提供了全面安全服务。特别 是使用领先的J2EE解决方案-Spring框架开发的企业软件项目。人们使用Spring Security有很多种原因,不过通常吸 引他们的是在J2EE Servlet规范或EJB规范中找不到典型企业应用场景的解决方案。 特别要指出的是他们不能再 WAR 或 EAR 级别进行移植。这样,如果你更换服务器环境,就要,在新的目标环境进行大量的工作,对你的应用 系统进行重新配 置安全。使用Spring Security 解决了这些问题,也为你提供很多有用的,完全可以指定的其他安 全特性。 安全包括两个主要操作。
“认证”,是为用户建立一个他所声明的主体。主题一般式指用户,设备或可以在你系 统中执行动作的其他系 统。
“授权”指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由 身份验证 过程建立了。
这些概念是通用的,不是Spring Security特有的。在身份验证层面,Spring Security广泛支持各种身份验证模式, 这些验证模型绝大多数都由第三方提供,或则正在开发的有关标准机构提供的,例如 Internet Engineering Task Force.作为补充,Spring Security 也提供了自己的一套验证功能。
Spring Security 目前支持认证一体化如下认证技术:
HTTP BASIC authentication headers (一个基于IEFT RFC 的 标准)
HTTP Digest authentication headers (一个基于IEFT RFC 的标准)
HTTP X.509 client certificate exchange (一个基于IEFT RFC 的标准)
LDAP (一个非常常见的跨平台认证需要做法,特别是在大环境)
Form-based authentication (提供简单用户接口的需求)
OpenID authentication Computer Associates Siteminder JA-SIG Central Authentication Service (CAS,这是一个流行的开源单点登录系统)
Transparent authentication context propagation for Remote Method Invocation and HttpInvoker (一个Spring远程调用协议)
Maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-webartifactId>
<version>5.0.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-configartifactId>
<version>5.0.1.RELEASEversion>
dependency>
dependencies>
创建一个新的maven工程,选择webapp模板,命名为springSecurityTest
<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.0modelVersion>
<groupId>com.pjy.springSecurityTestgroupId>
<artifactId>springSecurityTestartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<name>springSecurityTest Maven Webappname>
<url>http://www.example.comurl>
<properties>
<spring.version>5.0.2.RELEASEspring.version>
<spring.security.version>5.0.1.RELEASEspring.security.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-supportartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-webartifactId>
<version>${spring.security.version}version>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-configartifactId>
<version>${spring.security.version}version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.2version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.1version>
<configuration>
<port>8080port>
<path>/path>
configuration>
plugin>
plugins>
build>
project>
2、web.xml
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>SpringSecurity314display-name>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-security.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<filter>
<filter-name>springSecurityFilterChainfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
filter>
<filter-mapping>
<filter-name>springSecurityFilterChainfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<welcome-file-list>
<welcome-file>index.htmlwelcome-file>
<welcome-file>index.htmwelcome-file>
<welcome-file>index.jspwelcome-file>
<welcome-file>default.htmlwelcome-file>
<welcome-file>default.htmwelcome-file>
<welcome-file>default.jspwelcome-file>
welcome-file-list>
web-app>
3、spring-security配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http auto-config="true"
use-expressions="false">
<security:intercept-url pattern="/**"
access="ROLE_USER"/>
security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="{noop}user"
authorities="ROLE_USER"/>
<security:user name="admin" password="{noop}admin"
authorities="ROLE_ADMIN"/>
security:user-service>
security:authentication-provider>
security:authentication-manager>
beans>
4、测试
我们在webapp下创建一个index.html页面,在页面中任意写些内容。
启动Tomcat,访问页面,在输入设定好的账号信息。
当输入非正确数据时,显示错误(红色信息)
当输入user账户时,可以显示index.jsp界面,当输入admin账号时,显示的403错误界面,因为权限不足导致无法访问index界面
当我们访问index.html页面时发现会弹出登录窗口,可能你会奇怪,我们没有建立下面的登录页面,为什么Spring Security会跳到上面的登录页面呢?这是我们设置http的auto-config=”true”时Spring Security自动为我们生成的
5、使用自定义页面
5.1 spring-security.xml配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http security="none" pattern="/login.html" />
<security:http security="none" pattern="/failer.html" />
<security:http auto-config="true" use-expressions="false" >
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login login-page="/login.html"
login-processing-url="/login"
username-parameter="username"
password-parameter="password"
authentication-failure-url="/failer.html"
default-target-url="/success.html"
authentication-success-forward-url="/success.html"
/>
<security:csrf disabled="true" />
security:http>
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="{noop}user"
authorities="ROLE_USER" />
<security:user name="admin" password="{noop}admin"
authorities="ROLE_ADMIN" />
security:user-service>
security:authentication-provider>
security:authentication-manager>
beans>
5.2、配置前端界面
在webapp下login.html
<html>
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>head>
<body>
<form action="login" method="post">
<table>
<tr>
<td>用户名:td>
<td><input type="text" name="username"/>td>
tr>
<tr>
<td>密码:td>
<td><input type="password" name="password"/>td>
tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="登录"/> <input type="reset" value="重置"/>td>
tr>
table>
form>
body>
html>
在分别配置failer、success界面,在启动服务器,输入登录信息,
当输入正确时,跳转到success界面,由于设置了默认跳转success界面,当输入成功时,没有区分角色权限user、admin账户跳转到同一个界面中:
在Spring Security中如果想要使用数据进行认证操作,有很多种操作方式,这里我们介绍使用UserDetails、 UserDetailsService来完成操作。
UserDetails是一个接口,我们可以认为UserDetails作用是于封装当前进行认证的用户信息,但由于其是一个 接口,所以我们可以对其进行实现,也可以使用Spring Security提供的一个UserDetails的实现类User来完成 操作
以下是User类的部分代码
public class User implements UserDetails, CredentialsContainer {
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired; //帐户是否过期
private final boolean accountNonLocked; //帐户是否锁定
private final boolean credentialsNonExpired; //认证是否过期
private final boolean enabled; //帐户是否可用
}
UserDetailsService
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
上面将UserDetails与UserDetailsService做了一个简单的介绍,那么我们具体如何完成Spring Security的数据库认 证操作哪,我们通过用户管理中用户登录来完成Spring Security的认证操作。
spring security的配置
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
security:authentication-provider>
security:authentication-manager>
7.5.2 登录页面login.jsp
把登录界面login.jsp放在webapp下,设置表单的提交方式:
<form action="${pageContext.request.contextPath}/login.do" method="post">
<div class="form-group has-feedback">
<input type="text" name="username" class="form-control"
placeholder="用户名"> <span
class="glyphicon glyphicon-envelope form-control-feedback">span>
div>
<div class="form-group has-feedback">
<input type="password" name="password" class="form-control"
placeholder="密码"> <span
class="glyphicon glyphicon-lock form-control-feedback">span>
div>
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label><input type="checkbox"> 记住 下次自动登录label>
div>
div>
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">登录button>
div>
div>
form>
创建IUserService对象,继承UserDetailsService,创建接口实现类:UserServiceImpl,实现loadUserByUsername
@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = null;
try {
userInfo = userDao.findByUsername(username);
} catch (Exception e) {
e.printStackTrace();
}
//处理自己的用户对象封装成UserDetails
//User user = new User(userInfo.getUsername(), "{noop}"+userInfo.getPassword(),getAuthority(userInfo.getRoles()));
User user = new User(userInfo.getUsername(), "{noop}"+userInfo.getPassword(), userInfo.getStatus()==0 ? false:true,true,true,true,getAuthority(userInfo.getRoles()));
return user;
}
//作用就是返回一个List集合,集合中转入的是角色描述
public List<SimpleGrantedAuthority> getAuthority(List<Role> roles){
List<SimpleGrantedAuthority> list = new ArrayList<>();
for (Role role:roles){
list.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));
}
return list;
}
在Dao中添加IUserDao接口,添加findByUsername()方法,在查询中roles对象是一个多对多关系,创建IRoleDao接口类,添加findRoleByUserId()方法,根据用户id查询出所有对应的角色
public interface IUserDao {
@Select("select * from users where username=#{username}")
@Results({
@Result(id=true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "email", column = "email"),
@Result(property = "password", column = "password"),
@Result(property = "phoneNum", column = "phoneNum"),
@Result(property = "status", column = "status"),
@Result(property = "roles", column = "id", javaType = java.util.List.class, many = @Many(select = "com.pjy.ssm.dao.IRoleDao.findRoleByUserId")),
})
public UserInfo findByUsername(String username) throws Exception;
}
使用spring-security完成用户退出,非常简单
配置:
<security:logout invalidate-session="true" logout-url="/logout.do" logout-successurl="/login.jsp" />
页面中:
<a href="${pageContext.request.contextPath}/logout.do" class="btn btn-default btn-flat">注销a>
用户查询页面 user-list.jsp
1、添加user-list.jsp界面到pages中,设置表单提交的方式:
<c:forEach items="${userList}" var="user">
<tr>
<td><input name="ids" type="checkbox">td>
<td>${user.id }td>
<td>${user.username }td>
<td>${user.email }td>
<td>${user.phoneNum }td>
<td>${user.statusStr }td>
<td class="text-center">
<a href="${pageContext.request.contextPath}/user/findById.do?id=${user.id}" class="btn bg-olive btn-xs">详情a>
<a href="${pageContext.request.contextPath}/user/findUserByIdAndAllRole.do?id=${user.id}" class="btn bg-olive btn-xs">添加角色a>
td>
tr>
c:forEach>
在controller中创建UserController类,添加findAll()方法:
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<UserInfo> userList = userService.findAll();
mv.addObject("userList",userList);
mv.setViewName("user-list");
return mv;
}
}
2、Service
在IUserService中创建创建findAll()方法,并且在实现类中实现调用dao层中的findAll()方法:
@Override
public List<UserInfo> findAll() throws Exception {
return userDao.findAll();
}
3、Dao
在IUserDao中创建findAll()方法:
@Select("select * from users")
List<UserInfo> findAll() throws Exception;
重启服务器:
1、用户添加页面 user-add.jsp
在pages中添加user-add.jsp页面
在UserController中添加save()方法,保存用户信息:
//用户添加
@RequestMapping("save.do")
public String save(UserInfo userInfo) throws Exception {
userService.save(userInfo);
return "redirect:findAll.do";
}
3、Service
在UserServiceImpl实现类中,添加save()方法,在调用dao方法前,对password属性加密,采用SpringSecurity中的BCryptPasswordEncoder类进行MD5封装,生成一个随机“言”对象,保存在数据库中(注意是60个字节),在spring-security.xml中配置加密方式,修改UserServiceImpl实现类中"{noop}"+拼接。
/*
* 用户添加
* */
@Override
public void save(UserInfo userInfo) throws Exception{
//对密码加密
//在将加密后的数据封装到password中
userInfo.setPassword(bCryptPasswordEncoder.encode(userInfo.getPassword()));
userDao.save(userInfo);
}
前期我们的用户密码没有加密,现在添加用户时,我们需要对用户密码进行加密spring-security.xml
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<security:password-encoder ref="passwordEncoder"/>
security:authentication-provider>
security:authentication-manager>
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
项目中在Utils工具类中对BCryptPasswordEncoder方法进行了重写:
public class BCryptPasswordEncoderUtils {
//将springSecurity 中的BCryptPasswordEncoder提取出来单独使用
private static BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
public static String encodePassword(String password){
return bCryptPasswordEncoder.encode(password);
}
/*
* 测试加密结果
* */
public static void main(String[] args) {
String password = "123";
String pwd = encodePassword(password);
//通过 言 方式加密 每一次加密的key值都不一样
//$2a$10$Y2m2ZMobCsXJp2q23I0okuwoG/AtuKx.FksVUNGsJl7bnDasj1y.C
System.out.println(pwd);
}
}
4、dao
在IUserDao中添加save()方法,注意数据库字段问题(password字段要大于60个字节)
//添加用户
@Select("insert into users(email,username,password,phoneNum,status) values(#{email},#{username},#{password},#{phoneNum},#{status})")
public void save(UserInfo userInfo) throws Exception;
2、Controller
在controller中UserController类下,添加findById()方法,通过id查询用户信息
//查询指定id的用户
@RequestMapping("/findById")
public ModelAndView findById(String id) throws Exception {
ModelAndView mv = new ModelAndView();
UserInfo userInfo = userService.findById(id);
mv.addObject("user",userInfo);
mv.setViewName("user-show1");
return mv;
}
3、Service
调用UserServiceImpl实现类创建findById()方法,根据传递的id在dao层查询数据:
/*
* 根据Id查询用户所有信息
* */
@Override
public UserInfo findById(String id) throws Exception {
return userDao.findById(id);
}
4、Dao
//根据Id查询用户信息
@Select("select * from users where id=#{id}")
@Results({
@Result(id=true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "email", column = "email"),
@Result(property = "password", column = "password"),
@Result(property = "phoneNum", column = "phoneNum"),
@Result(property = "status", column = "status"),
@Result(property = "roles", column = "id", javaType = java.util.List.class, many = @Many(select = "com.pjy.ssm.dao.IRoleDao.findRoleByUserId")),
})
UserInfo findById(String id) throws Exception;
我们需要将用户的所有角色及权限查询出来所以需要调用IRoleDao中的findRoleByUserId,而在IRoleDao中需要调用IPermissionDao的findByRoleId
//根据用户id查询出所有对应的角色
@Select("select * from role where id in(select roleId from users_role where userId=#{userId})")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "roleName", column = "roleName"),
@Result(property = "roleDesc", column = "roleDesc"),
@Result(property = "permissions", column = "id", javaType = java.util.List.class,many = @Many(select = "com.pjy.ssm.dao.IPermissionDao.findPermissionByRoleId"))
})
public List<Role> findRoleByUserId(String userId) throws Exception;
重启服务器,登录查询:
创建RoleController,添加findAll()方法:
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<Role> roleList = roleService.findAll();
mv.addObject("roleList",roleList);
mv.setViewName("role-list");
return mv;
}
/*
* 查询所有角色信息
* */
@Override
public List<Role> findAll() throws Exception {
return roleDao.findAll();
}
/*
* 查询所有角色信息
* */
@Select("select * from role")
public List<Role> findAll() throws Exception;
重新打包,启动服务器:
@RequestMapping("/save.do")
public String save(Role role) throws Exception {
roleService.save(role);
//添加完成以后重定向到findAll.do
return "redirect:findAll.do";
}
/*
* 添加角色信息
* */
@Override
public void save(Role role) throws Exception {
roleDao.save(role);
}
/*
* 新增角色信息
* */
@Insert("insert into role(roleName, roleDesc) values(#{roleName},#{roleDesc})")
public abstract void save(Role role) throws Exception;
重启服务器:
创建PermissionController类,创建findAll()方法,调用service,查询:
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<Permission> permissionList = permissionService.findAll();
mv.addObject("permissionList",permissionList);
mv.setViewName("permission-list");
return mv;
}
9.1.3 Dao
调用dao中的
/*
* 查询所有权限
* */
@Select("select * from permission")
public List<Permission> findAll() throws Exception;
重启服务器:
请在资料中查看页面详细代码
@RequestMapping("/save.do")
public String save(Permission permission) throws Exception{
permissionService.save(permission);
return "redirect:findAll.do";
}
9.2.3 Dao
/*
* 添加资源权限
* */
@Insert("insert into permission(permissionName, url) values(#{permissionName}, #{url})")
public abstract void save(Permission permission) throws Exception;
重启服务器:
用户与角色之间是多对多关系,我们要建立它们之间的关系,只需要在中间表user_role插入数据即可。
<a href="${pageContext.request.contextPath}/user/findUserByIdAndAllRole.do?id=${user.id}" class="btn bg-olive btn-xs">添加角色</a>
展示可以添加角色的页面user-roe-add.jsp
findUserByIdAndAllRole(Long id)方法
此方法用于查找要操作的用户及可以添加的角色,参数是要操作的用户id
/*
* 查询用户以及用户可以添加的角色
* */
@RequestMapping("/findUserByIdAndAllRole.do")
public ModelAndView findUserByIdAndAllRole(@RequestParam(name = "id", required = true) String userId) throws Exception {
ModelAndView mv = new ModelAndView();
//1.根据用户id查询用户
UserInfo userInfo = userService.findById(userId);
//2.根据用户Id查询可以添加的角色
List<Role> otherRoles = userService.findOtherRoles(userId);
mv.addObject("user",userInfo);
mv.addObject("roleList",otherRoles);
mv.setViewName("user-role-add");
return mv;
}
调用IUserService的findById方法获取要操作的User
调用IRoleService的findOtherRole方法用于获取可以添加的角色信息
addRoleToUser(Long userId,Long[] ids)方法
方法用于在用户与角色之间建立关系,参数userId代表要操作的用户id,参数ids代表的是角色id数组
/*
* 给用户添加角色
* userId:你要添加用户的id
* ids:要添加的角色的id
* */
@RequestMapping("/addRoleToUser.do")
public String addRoleToUser(@RequestParam(name = "userId",required = true) String userId, @RequestParam(name = "ids",required = true) String[] roleIds) throws Exception {
userService.addRoleToUser(userId, roleIds);
return "redirect:findAll.do";
}
IRoleDao
/*
* 查询可以添加的角色信息
* 子查询:当前用户包含的角色信息
* 查询:不包含子查询的角色信息
* */
@Select("select * from role where id not in (select roleId from users_role where userId=#{id})")
List<Role> findOtherRoles(String userId) throws Exception;
IUserDao
/*
* 根据Id添加用户角色
* */
@Insert("insert into users_role(userId, roleId) values(#{userId}, #{roleId})")
void addRoleToUser(@Param("userId") String userId, @Param("roleId") String roleId) throws Exception;
重启服务器:
角色与权限之间是多对多关系,我们要建立它们之间的关系,只需要在中间表role_permission插入数据即可。
在role-list.jsp页面上添加链接
<a href="${pageContext.request.contextPath}/user/findUserByIdAndAllRole.do?id=${user.id}" class="btn bg-olive btn-xs">添加权限</a>
展示可以添加权限的页面roe-permission-add.jsp
findRoleByIdAndAllPermission(Long roleId)方法
此方法用于查找要操作的角色及可以添加的权限,参数是要操作的角色id
/*
* 根据roleId查询role,并查询出可以添加的权限
* */
@RequestMapping("/findRoleByIdAndAllPermission.do")
public ModelAndView findRoleByIdAndAllPermission(@RequestParam(name = "id",required = true) String roleId) throws Exception {
ModelAndView mv = new ModelAndView();
//根据roleId查询role
Role role = roleService.findById(roleId);
//根据roleId查询可以添加的权限
List<Permission> otherPermissions = roleService.findOtherPermissions(roleId);
mv.addObject("role",role);
mv.addObject("permissionList",otherPermissions);
mv.setViewName("role-permission-add");
return mv;
}
调用IRoleService的findById方法获取要操作的Role
调用IPermissionService的findOtherPermission方法用于获取可以添加的权限信息
addPermissionToRole(Long roleId,Long[] ids)方法
此方法用于在角色与权限之间建立关系,参数roleId代表要操作的角色id,参数permissionIds代表的是权限id数组
/*
* 给角色添加权限
* */
@RequestMapping("/addPermissionToRole.do")
public String addPermissionToRole(@RequestParam(name = "roleId",required = true) String roleId,@RequestParam(name = "ids",required = true) String[] permissionIds) throws Exception{
roleService.addPermissionToRle(roleId, permissionIds);
return "redirect:findAll.do";
}
在RoleServiceImpl中添加findOtherPermissions()方法实现,传入roleId,调用Dao方法
/*
* 查询当前用户没有的权限信息
* */
@Override
public List<Permission> findOtherPermissions(String roleId) throws Exception {
return roleDao.findOtherPermissions(roleId);
}
在RoleServiceImpl中添加addPermissionToRle()方法,传入角色id,权限id遍历权限数组,将数组中的元素插入中间表中:
/*
* 为当前id下的用户添加权限
* */
@Override
public void addPermissionToRle(String roleId, String[] permissionIds) throws Exception {
for (String permissionId : permissionIds) {
roleDao.addPermissionToRole(roleId,permissionId);
}
}
IPermissionDao
/*
* 查询当前用户没有关联的权限信息
* */
@Select("select * from permission where id not in (select permissionId from role_permission where roleId=#{roleId})")
List<Permission> findOtherPermissions(String roleId) throws Exception;
用于查找可以添加的权限
IRoleDao
/*
* 在中间表role_permission中查询权限URL
* */
@Insert("insert into role_permission(roleId, permissionId) values(#{roleId},#{permissionId})")
void addPermissionToRole(@Param("roleId") String roleId, @Param("permissionId") String permissionId) throws Exception;
用于绑定角色与权限的关系
在服务器端我们可以通过Spring security提供的注解对方法来进行权限控制。Spring Security在方法的权限控制上 支持三种类型的注解,JSR-250注解、@Secured注解和支持表达式的注解,这三种注解默认都是没有启用的,需要 单独通过global-method-security元素的对应属性进行启用
配置文件
<security:global-method-security jsr250-annotations="enabled"/>
<security:global-method-security secured-annotations="enabled"/>
<security:global-method-security pre-post-annotations="disabled"/>
注解开启
@EnableGlobalMethodSecurity :Spring Security默认是禁用注解的,要想开启注解,需要在继承 WebSecurityConfigurerAdapter的类上加@EnableGlobalMethodSecurity注解,并在该类中将 AuthenticationManager定义为Bean。
@RolesAllowed表示访问对应方法时所应该具有的角色
示例:
@RolesAllowed({"USER", "ADMIN"}) 该方法只要具有"USER", "ADMIN"任意一种权限就可以访问。这里可以省 略前缀ROLE_,实际的权限可能是ROLE_ADMIN
@PermitAll表示允许所有的角色进行访问,也就是说不进行权限控制
@DenyAll是和PermitAll相反的,表示无论什么角色都不能访问
@PreAuthorize 在方法调用之前,基于表达式的计算结果来限制对方法的访问
示例:
@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)") void changePassword(@P("userId") long userId ){ } 这里表示在changePassword方法执行之前,判断方法参数userId的值是否等于principal中保存的当前用户的 userId,或者当前用户是否具有ROLE_ADMIN权限,两种符合其一,就可以访问该方法。
@PostAuthorize 允许方法调用,但是如果表达式计算结果为false,将抛出一个安全性异常
示例:
@PostAuthorize User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");
@PostFilter 允许方法调用,但必须按照表达式来过滤方法的结果
@PreFilter 允许方法调用,但必须在进入方法之前过滤输入值
@Secured注解标注的方法进行权限控制的支持,其值默认为disabled。
示例:
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("ROLE_TELLER")
在jsp页面中我们可以使用spring security提供的权限标签来进行权限控制
maven导入
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-taglibsartifactId>
<version>versionversion>
dependency>
页面导入
<%@taglib uri="http://www.springframework.org/security/tags" prefix="security"%>
在jsp中我们可以使用以下三种标签,其中authentication代表的是当前认证对象,可以获取当前认证对象信息,例 如用户名。其它两个标签我们可以用于权限控制
<security:authentication property="" htmlEscape="" scope="" var=""/>
property: 只允许指定Authentication所拥有的属性,可以进行属性的级联获取,如“principle.username”, 不允许直接通过方法进行调用
htmlEscape:表示是否需要将html进行转义。默认为true。
scope:与var属性一起使用,用于指定存放获取的结果的属性名的作用范围,默认我pageContext。Jsp中拥有的作用范围都进行进行指定
var: 用于指定一个属性名,这样当获取到了authentication的相关信息后会将其以var指定的属性名进行存 放,默认是存放在pageConext中
authorize是用来判断普通权限的,通过判断用户是否具有对应的权限而控制其所包含内容的显示
<security:authorize access="" method="" url="" var="">security:authorize>
access: 需要使用表达式来判断权限,当表达式的返回结果为true时表示拥有对应的权限
用户管理
method:method属性是配合url属性一起使用的,表示用户应当具有指定url指定method访问的权限, method的默认值为GET,可选值为http请求的7种方法
url:url表示如果用户拥有访问指定url的权限即表示可以显示authorize标签包含的内容
var:用于指定将权限鉴定的结果存放在pageContext的哪个属性中
accesscontrollist标签是用于鉴定ACL权限的。其一共定义了三个属性:hasPermission、domainObject和var, 其中前两个是必须指定的
<security:accesscontrollist hasPermission="" domainObject="" var="">security:accesscontrollist>
hasPermission:hasPermission属性用于指定以逗号分隔的权限列表
domainObject:domainObject用于指定对应的域对象
var:var则是用以将鉴定的结果以指定的属性名存入pageContext中,以供同一页面的其它地方使用
序号 | 字段名 | 字段类型 | 字段描述 |
---|---|---|---|
1 | id | varchar2(50) | 无意义,主键uuid |
2 | visitTime | timestamp | 访问时间 |
3 | username | varchar2(500) | 操作者用户名 |
4 | ip | varchar(50) | 访问ip |
5 | url | varchar2(20) | 访问资源url |
6 | executionTime | int | 执行时长 |
7 | method | varchar(20) | 访问方法 |
CREATE TABLE sysLog(
id VARCHAR2(32) default SYS_GUID() PRIMARY KEY,
visitTime timestamp,
username VARCHAR2(50),
ip VARCHAR2(30),
url VARCHAR2(50),
executionTime int,
method VARCHAR2(200)
)
public class SysLog {
private String id;
private Date visitTime;
private String visitTimeStr;
private String username;
private String ip;
private String url;
private Long executionTime;
private String method;
}
详细内容请查看资源中页面信息
package com.pjy.ssm.controller;
import com.pjy.ssm.domain.SysLog;
import com.pjy.ssm.service.ISysLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
/*
* 使用AOP中的切面配置日志
* */
@Component
@Aspect
public class LogAop {
private Date visitTime; //开始时间
private Class clazz; //访问的类
private Method method; //访问的方法
@Autowired
private HttpServletRequest request;
@Autowired
private ISysLogService sysLogService;
//前置通知——获取开始的时间,执行的类是哪一个,执行的方法是哪一个
//拦截controller下的所有方法
@Before("execution(* com.pjy.ssm.controller.*.*(..))")
public void doBefore(JoinPoint jp) throws NoSuchMethodException {
visitTime = new Date(); //当前时间就是开始访问的时间
clazz = jp.getTarget().getClass(); //具体要访问的类
String methodName = jp.getSignature().getName(); //获取访问的方法的名称
Object[] args = jp.getArgs();//获取访问的方法的参数
//获取具体执行的方法的Method对象
if (args==null || args.length==0){
method = clazz.getMethod(methodName); //只能获取无参数的方法
}else {
Class[] classArgs = new Class[args.length];
for (int i = 0; i < args.length; i++){
classArgs[i] = args[i].getClass();
}
clazz.getMethod(methodName,classArgs);
}
}
//后置通知
@After("execution(* com.pjy.ssm.controller.*.*(..))")
public void doAfter(JoinPoint jp) throws Exception {
//获取访问的时常
long time = new Date().getTime() - visitTime.getTime();
String url = "";
//获取url
if (clazz != null && method != null && clazz != LogAop.class){
//1.获取类上的@requestMapping("/orders")
RequestMapping classAnnotation = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
if (classAnnotation != null){
String[] classValue = classAnnotation.value();
//2.获取方法上的RequestMapping()值
RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
if (methodAnnotation != null){
String[] methodValue = methodAnnotation.value();
url = classValue[0] + methodValue[0];
//获取访问的IP地址
String ip = request.getRemoteAddr();
//获取当前操作的用户
//从上下文中获取当前登录的用户
SecurityContext context = SecurityContextHolder.getContext();
User user = (User) context.getAuthentication().getPrincipal();
String username = user.getUsername();
//将日志相关信息封装到SYSLog对象
SysLog sysLog = new SysLog();
sysLog.setExecutionTime(time);
sysLog.setIp(ip);
sysLog.setMethod("[类名] "+clazz.getName()+"[方法名] "+method.getName());
sysLog.setUrl(url);
sysLog.setUsername(username);
sysLog.setVisitTime(visitTime);
//调用service完成日志的记录
sysLogService.save(sysLog);
}
}
}
}
}
在切面类中我们需要获取登录用户的username,还需要获取ip地址,我们怎么处理?
username获取
SecurityContextHolder获取
ip地址获取
ip地址的获取我们可以通过request.getRemoteAddr()方法获取到。
在Spring中可以通过RequestContextListener来获取request或session对象。
package com.pjy.ssm.controller;
import com.pjy.ssm.domain.SysLog;
import com.pjy.ssm.service.ISysLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/sysLog")
public class SysLogController {
@Autowired
private ISysLogService sysLogService;
@RequestMapping("/findAll.do")
public ModelAndView findAll() throws Exception {
ModelAndView mv = new ModelAndView();
List<SysLog> sysLogList = sysLogService.findAll();
mv.addObject("sysLogs",sysLogList);
mv.setViewName("syslog-list");
return mv;
}
}
package com.pjy.ssm.service.impl;
import com.pjy.ssm.dao.ISysLogDao;
import com.pjy.ssm.domain.SysLog;
import com.pjy.ssm.service.ISysLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class SysLogServiceImpl implements ISysLogService {
@Autowired
private ISysLogDao sysLogDao;
@Override
public void save(SysLog sysLog) throws Exception {
sysLogDao.save(sysLog);
}
@Override
public List<SysLog> findAll() throws Exception {
return sysLogDao.findAll();
}
}
package com.pjy.ssm.dao;
import com.pjy.ssm.domain.SysLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface ISysLogDao {
/*
* 将日志操作保存到数据库中
* */
@Insert("insert into syslog(visitTime,username,ip,url,executionTime,method) values(#{visitTime},#{username},#{ip},#{url},#{executionTime},#{method})")
public void save(SysLog sysLog) throws Exception;
/*
* 查询所有日志记录
* */
@Select("select * from sysLog")
List<SysLog> findAll throws Exception;
}