第一步:准备依赖
1、Spring-core beans expression context aop tx jdbc web webmvc
2、Druid MySQL-connector-java mybatis mybatis-spring
3、 Log4j slf4j-api slf4j-log4j12
4、Jstl servlet-api Lombok aspectjweaver
第二步:项目准备
1、数据库准备,准备好测试要用的表及数据
2、架构设计
3、bean中的类(属性与数据库中的表对应)
@Setter@Getter@AllArgsConstructor@NoArgsConstructor@ToString public class User { private Integer id; private String username; private String pwd; @DateTimeFormat(pattern = "yyyy-MM-dd") private Date date; public String getDate(){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(this.date); } }
条件查询时封装的条件类
@Setter@Getter@ToString public class QueryBean { private String username; @DateTimeFormat(pattern = "yyyy-MM-dd") private String date; private Integer currentPage; private Integer pageSize; //修改set方法可以在接收前端参数进行封装时进行判断修改 public void setCurrentPage(Integer currentPage){ if(currentPage!=null){ this.currentPage=currentPage; }else{ this.currentPage=1; } } public void setPageSize(Integer pageSize){ if(pageSize!=null){ this.pageSize=pageSize; }else{ this.pageSize=3; } } //重写get方法,对多条件的结果进行预判断,可以减少xml中配置的代码 public String getUsername(){ return StringUtils.isNullOrEmpty(this.username)?null:this.username; } public String getDate(){ return StringUtils.isNullOrEmpty(this.date)?null:this.date; } //使用getStart方法可以返回start属性,在定义分页工具查询时使用 public Integer getStart(){ return (this.currentPage-1)*this.pageSize; } /*如果使用date类型的date,可以将get方法返回的结果转换格式返回 public String getDate(){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(this.date); }*/ }
4、mapper接口,以及对应方法的xml文件
public interface UserMapper { //增删改查 Integer save(User user); Integer update(User user); Integer delete(Integer id); User findById(Integer id); List<User> findAll(); //条件查询用到的方法 List<User> select(QueryBean queryBean); //登录检查需要用到的方法 String login(String username); //手动实现分页需要用到的方法 Integer selectCount(QueryBean queryBean); List<User> selectAll(QueryBean queryBean); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.test.ssm.mapper.UserMapper"> <insert id="save"> insert into users values(null,#{username},#{pwd},#{date}) </insert> <update id="update"> update users set username=#{username},pwd=#{pwd},date=#{date} where id=#{id} </update> <delete id="delete"> delete from users where id=#{id} </delete> <resultMap id="myResultMap" type="user"> <id property="id" column="id"/> <result column="username" property="username"/> <result column="pwd" property="pwd"/> <result column="date" property="date"/> </resultMap> <select id="findById" resultMap="myResultMap"> select * from users where id=#{id} </select> <select id="findAll" resultMap="myResultMap"> select * from users </select> <select id="select" resultMap="myResultMap"> select * from users <where> <!--在条件类中将get方法封装后可以简化的代码 <if test="username!=null and username!=''"> and username like concat('%',#{username},'%') </if> <if test="date!=null and date!=''"> and date like concat('%',#{date},'%') </if>--> <if test="username!=null"> and username like concat('%',#{username},'%') </if> <if test="date!=null"> and date like concat('%',#{date},'%') </if> </where> </select> <!--手动实现分页需要用到的查询,查询总条数--> <select id="selectCount" resultType="integer"> select count(*) from users <where> <if test="username!=null"> and username like concat('%',#{username},'%') </if> <if test="date!=null"> and date like concat('%',#{date},'%') </if> </where> </select> <!--手动实现分页需要用到的查询,条件查询--> <select id="selectAll" resultMap="myResultMap"> select * from users <where> <if test="username!=null"> and username like concat('%',#{username},'%') </if> <if test="date!=null"> and date like concat('%',#{date},'%') </if> </where> limit #{start},#{pageSize} </select> <!--登录检测需要用到的查询--> <select id="login" resultType="java.lang.String"> select pwd from users where username=#{username} </select> </mapper>
5、service接口,以及实现类(使用mapper对象)
传统的方式是
1:获取Factory对象,new SqlSessionFactoryBuilder().build(输入流);
2:获取SqlSession对象,Factory.openSession()
3:获取mapper对象,SqlSession.getMapper()
整合后的方式为
1.配置MapperFactoryBean(一次只能创建一个) mapperScannerConfigurer(批量创建,从配置的包中扫描所有赛接口,并且创建代理对象)
2.两个属性,一个是要创建的接口<property name="mapperInterface" value=""/>,一个是sqlsessionfactory
3.配置sqlsessionfactory,几个主要属性
数据源dataSource
配置别名typeAliasesPackage
关联mapper映射文件mapperLocations
关联mybatis的主配置文件configLocation
<?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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.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:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--数据源--> <property name="dataSource" ref="dataSource"/> <!--配置别名--> <property name="typeAliasesPackage" value="com.test.ssm.bean"/> <!--关联mapper映射文件--> <property name="mapperLocations" value="classpath:com/test/ssm/mapper/*Mapper.xml"/> <!--关联mybatis的主配置文件--> <property name="configLocation" value="classpath:mybatis.xml"/> </bean> <!--<bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.test.ssm.mapper.UserMapper"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <bean id="service" class="com.test.ssm.service.impl.UserServiceImpl"> <property name="userMapper" ref="mapperFactory"/> </bean>--> <!--配置mapper接口扫描器MapperScannerConfigurer 会从配置的包中,扫描所有的接口,并且创建接口的代理对象 下面的bean等同与上面注释的两个的作用 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.test.ssm.mapper"/> <!--当配置文件中只有一个数据源的时候,可以不写这个参数 有多个数据源的时候,需要指定使用哪个数据源--> <!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />--> </bean> <!--IoC注解扫描--> <context:component-scan base-package="com.test.ssm"/> <!--事务管理器--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--使用注解方式只需要配置事务注解驱动,然后将事务管理器进行绑定 在需要事务控制的类上贴上@Transactional即可--> <!--<aop:config> <aop:pointcut id="pointcut" expression="execution( * com.test.ssm.service..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> </aop:config> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="find*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice>--> <!--事务管理器注解驱动--> <tx:annotation-driven transaction-manager="txManager"/> </beans>
public interface UserMapper { //增删改查 Integer save(User user); Integer update(User user); Integer delete(Integer id); User findById(Integer id); List<User> findAll(); //条件查询用到的方法 List<User> select(QueryBean queryBean); //登录检查需要用到的方法 String login(String username); //手动实现分页需要用到的方法 Integer selectCount(QueryBean queryBean); List<User> selectAll(QueryBean queryBean); }
@Service @Transactional public class UserServiceImpl implements IUserService { @Autowired private UserMapper userMapper; public Integer save(User user) { return userMapper.save(user); } public Integer update(User user) { Integer update = userMapper.update(user); //System.out.println(1/0); return update; } public Integer delete(Integer id) { return userMapper.delete(id); } public User findById(Integer id) { return userMapper.findById(id); } public List<User> findAll() { return userMapper.findAll(); } public Boolean login(String username, String pwd) { String login = userMapper.login(username); if(login.equals(pwd)){ return true; } return false; } public List<User> select(QueryBean queryBean) { return userMapper.select(queryBean); } //分页插件的使用 public PageInfo<User> select2(QueryBean queryBean) { PageHelper.startPage(queryBean.getCurrentPage(),queryBean.getPageSize()); List<User> list = userMapper.select(queryBean); PageInfo<User> pageInfo = new PageInfo<>(list); return pageInfo; } /*将属性封装到类中,减少业务端代码 public PageUtils select3(QueryBean queryBean) { Integer total = userMapper.selectCount(queryBean); PageUtils pageUtils = new PageUtils(); //从前台取出的数据赋值 pageUtils.setPageNum(queryBean.getCurrentPage()); pageUtils.setPageSize(queryBean.getPageSize()); //从数据库中查询出的数据赋值 pageUtils.setList(userMapper.selectAll(queryBean)); pageUtils.setTotal(total); //经过计算得到的数据赋值 int pages = Double.valueOf(Math.ceil(pageUtils.getTotal()*1.0/pageUtils.getPageSize())).intValue(); pageUtils.setPages(pages); Integer nextPage; if(queryBean.getCurrentPage()==pageUtils.getPages()){ nextPage=1; }else{ nextPage=queryBean.getCurrentPage()+1; } pageUtils.setNextPage(nextPage); Integer prePage; if(queryBean.getCurrentPage()==1){ prePage=pageUtils.getPages(); }else{ prePage=queryBean.getCurrentPage()-1; } pageUtils.setPrePage(prePage); return pageUtils; }*/ public PageUtils select3(QueryBean queryBean) { Integer total = userMapper.selectCount(queryBean); if(total==0){ //可以减少一次查询,提高效率 return new PageUtils(queryBean.getCurrentPage(),queryBean.getPageSize(),null,total); } List<User> list = userMapper.selectAll(queryBean); return new PageUtils(queryBean.getCurrentPage(),queryBean.getPageSize(),list,total); } }
6、这时可先测试一下代码,使用测试类App测试增删改查
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class App { @Autowired private IUserService userService; @Test public void testSave(){ User user = new User(null,"林丹","qwer",new Date()); userService.save(user); } @Test public void testUpdate(){ User user =new User(10,"小芳","qwer",new Date()); userService.update(user); } @Test public void testDelete(){ userService.delete(5); } @Test public void testFindOneById(){ System.out.println(userService.findById(3)); } @Test public void testFindAll(){ System.out.println(userService.findAll()); } }
第三步:添加事务控制
1、添加依赖 aspectj
2、配置事务管理器,本质上是一个<bean id="txManager" class=""/>
3、配置事务(两种方式)
1.xml方式
<aop:config> <aop:pointcut id="pointcut" expression="execution( * com.test.ssm.service..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> </aop:config> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="find*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice>
2.使用注解的方式
配置事务管理器,需要配置事务管理器注解驱动@Transactional即可
<!--事务管理器注解驱动--> <tx:annotation-driven transaction-manager="txManager"/>
第四步:配置前端页面
1、导入依赖servlet-api
2、在web.xml中配置前端控制器(本质上是一个servlet)
客户端访问时会先通过前端控制器,再通过拦截器
<!--配置前端控制器--> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--配置有请求访问时加载的文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> <!--配置自动启动前端控制器--> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
注意:配置中的文件关联有没有将配置信息引入,可以在springMVC中使用import标签进行引入
<?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: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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--引入后台的Spring配置文件--> <import resource="classpath:applicationContext.xml"/> </beans>
3、在web.xml中配置编码过滤器,解决中文乱码问题
<!--请求编码过滤器--> <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>
4、在springMVC中配置拦截器,拦截想要跳过登录界面访问受保护资源的请求
<!--拦截器--> <mvc:interceptors> <!--登录检查拦截器--> <!--路径/**表示拦截所有路径,/*只能拦截所有一级路径--> <mvc:interceptor> <mvc:mapping path="/**"/> <!--对某个请求放行--> <mvc:exclude-mapping path="/login.do"/> <!--配置拦截器的全限定名--> <bean class="com.test.ssm.interceptor.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
拦截器
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); if(session.getAttribute("USER_IN_SESSION")==null){ response.sendRedirect("/index.html"); return false; } return true; } }
第五步:编写Controller和访问页面
在web.xml中添加如下代码,可以默认访问此页面
<welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list>
Controller代码示例
@Controller public class UserController { @Autowired private IUserService userService; @RequestMapping("/findAll") public String findAll(Model model){ List<User> list = userService.findAll(); model.addAttribute("list",list); return "list"; } @RequestMapping("/login") public String login(String username, String pwd, HttpSession session){ Boolean login = userService.login(username, pwd); if(login){ session.setAttribute("USER_IN_SESSION",username); return "redirect:/findAll.do"; } return "redirect:/index.html"; } @RequestMapping("/edit") public String edit(Integer id,Model model){ if(id!=null){ model.addAttribute("user",userService.findById(id)); } return "edit"; } //将添加和保存写在同一个页面内 @RequestMapping("/saveOrUpdate") public String saveOrUpdate(User user){ if(user.getId()!=null){ userService.update(user); }else{ userService.save(user); } return "redirect:/findAll.do"; } @RequestMapping("/delete") public String delete(Integer id){ if(id!=null){ userService.delete(id); } return "redirect:/findAll.do"; } //条件查询 @RequestMapping("/select") public String select(@ModelAttribute("queryBean") QueryBean queryBean, Model model){ List<User> list = userService.select(queryBean); model.addAttribute("list",list); return "list"; } //使用分页插件进行条件查询 @RequestMapping("/select2") public String select2(@ModelAttribute("queryBean") QueryBean queryBean, Model model){ PageInfo<User> pageInfo = userService.select2(queryBean); model.addAttribute("page",pageInfo); return "list"; } //使用自己定义的分页功能进行条件查询 @RequestMapping("/select3") public String select3(@ModelAttribute("queryBean") QueryBean queryBean, Model model){ PageUtils pageUtils = userService.select3(queryBean); model.addAttribute("page",pageUtils); return "list"; } }
页面代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>Hello World!</h2> <form action="/login.do" method="post"> 用户名:<input type="text" name="username"/> 密码:<input type="text" name="pwd"/> <input type="submit"/> </form> </body> </html>
下面两个页面在WEB-INF下的views里,是受保护页面,list中现在还没有分页功能,将page.list替换为list即可,分页功能在下篇博客中详细说明。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <div style="text-align: center;"> <h2>用户编辑</h2> <form action="/saveOrUpdate.do" method="post"> <input type="hidden" name="id" value="${user.id}"/> 用户名:<input type="text" name="username" value="${user.username}"/> <br/>密码:<input type="text" name="pwd" value="${user.pwd}"/><br/> 日期:<input type="date" name="date" value="${user.date}"/><br/> <input type="submit" value="保存"/> </form> </div> </body> </html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> <script type="text/javascript"> function goPage(page) { //设置当前页的值 document.getElementById("currentPage").value = page; //提交表单 var form = document.getElementById("f1"); form.submit(); //提交表单 } </script> </head> <body> <div style="text-align: center;"> <h1>用户列表</h1> <%--使用了对象进行参数接受的时候,在参数封装的同时会将对象加入到作用域中 --%> <form id="f1" action="/select3.do" method="post"> 用户姓名:<input type="text" name="username" value="${queryBean.username}"/> 注册日期:<input type="text" name="date" value="${queryBean.date}"/> <input type="submit" value="查询"/> <input type="button" onclick="location.href='/edit.do'" value="增加" style="width: 40px;text-align: center"/> <table border="1px" width="60%" style="text-align: center;margin: 0 auto;"> <tr> <th>编号</th> <th>姓名</th> <th>密码</th> <th>注册日期</th> <th>操作</th> </tr> <c:forEach items="${page.list}" var="user" varStatus="index"> <tr> <td>${index.count}</td> <td>${user.username}</td> <td>${user.pwd}</td> <td>${user.date}</td> <td><a href="/edit.do?id=${user.id}">修改</a>|<a href="/delete.do?id=${user.id}">删除</a></td> </tr> </c:forEach> <tr> <td colspan="5"> <a href="javascript:void(0);" onclick="goPage(1)">首页</a> <a href="javascript:void(0);" onclick="goPage(${page.prePage})">上一页</a> <a href="javascript:void(0);" onclick="goPage(${page.nextPage})">下一页</a> <a href="javascript:void(0);" onclick="goPage(${page.pages})">尾页</a> <input type="text" name="currentPage" id="currentPage"/> <input type="submit" value="跳转"/> 每页显示: <select name="pageSize" onchange="goPage(1)"> <option value="3" ${queryBean.pageSize==3?'selected':''}>3</option> <option value="4" ${queryBean.pageSize==4?'selected':''}>4</option> <option value="5" ${queryBean.pageSize==5?'selected':''}>5</option> </select> 当前${page.pageNum}页/共${page.pages}页/共${page.total}条记录 </td> </tr> </table> </form> </div> </body> </html>
第六步:加入多条件查询
可以将条件封装到一个类中的属性上
@Setter@Getter@ToString public class QueryBean { private String username; @DateTimeFormat(pattern = "yyyy-MM-dd") private String date; private Integer currentPage; private Integer pageSize; //修改set方法可以在接收前端参数进行封装时进行判断修改 public void setCurrentPage(Integer currentPage){ if(currentPage!=null){ this.currentPage=currentPage; }else{ this.currentPage=1; } } public void setPageSize(Integer pageSize){ if(pageSize!=null){ this.pageSize=pageSize; }else{ this.pageSize=3; } } //重写get方法,对多条件的结果进行预判断,可以减少xml中配置的代码 public String getUsername(){ return StringUtils.isNullOrEmpty(this.username)?null:this.username; } public String getDate(){ return StringUtils.isNullOrEmpty(this.date)?null:this.date; } //使用getStart方法可以返回start属性,在定义分页工具查询时使用 public Integer getStart(){ return (this.currentPage-1)*this.pageSize; } /*如果使用date类型的date,可以将get方法返回的结果转换格式返回 public String getDate(){ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(this.date); }*/ }
sql查询语句select * from users where name like concat('%',#{},'%'),concat的作用是合并字符串
<select id="select" resultMap="myResultMap"> select * from users <where> <!--在条件类中将get方法封装后可以简化的代码 <if test="username!=null and username!=''"> and username like concat('%',#{username},'%') </if> <if test="date!=null and date!=''"> and date like concat('%',#{date},'%') </if>--> <if test="username!=null"> and username like concat('%',#{username},'%') </if> <if test="date!=null"> and date like concat('%',#{date},'%') </if> </where> </select>