博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SSM框架整合
阅读量:6306 次
发布时间:2019-06-22

本文共 22724 字,大约阅读时间需要 75 分钟。

环境

  • Windows10
  • JDK1.8
  • MySql5.5

工具

  • Maven3.5
  • IDEA2017
  • Navicat(数据库连接工具)

准备工作

步骤

1、新建Maven webapp项目

clipboard.png

clipboard.png
clipboard.png

建好基本目录,按住 Ctrl+Alt+Shift+s配置目录,如下图

clipboard.png

新建几个包,如下图:

clipboard.png

包名 名称 作用
dao 数据访问层(接口 与数据操作有关的都放在这里
entity 实体类 一般与数据库的表相对应,封装dao层取出来的数据为一个对象,也就是我们常说的pojo,一般只在dao层与service层之间传输
dto 数据传输层 用于service层与web层之间传输,相当于vo
service 业务逻辑(接口) 写我们的业务逻辑,在设计业务接口时候应该站在“使用者”的角度。
serviceImpl 业务逻辑(实现) 实现我们业务接口,一般事务控制是写在这里,没什么好说的。
controller 控制器 springmvc就是在这里发挥作用的

2、配置文件

配置完成的目录如下:

clipboard.png

  1. pom.xml

    4.0.0
    com.messchx
    ssmdemo
    war
    1.0-SNAPSHOT
    ssmdemo Maven Webapp
    http://maven.apache.org
    ssmdemo
    org.mortbay.jetty
    maven-jetty-plugin
    6.1.7
    8888
    30000
    ${project.build.directory}/${pom.artifactId}-${pom.version}
    /
    org.mybatis.generator
    mybatis-generator-maven-plugin
    1.3.2
    org.apache.maven.plugins
    maven-compiler-plugin
    1.8
    1.8
    src/main/java
    **/*.xml
    **/*.properties
    src/main/resources
    **/*.xml
    **/*.properties
    UTF-8
    UTF-8
    4.3.5.RELEASE
    3.4.1
    javax
    javaee-api
    7.0
    junit
    junit
    4.12
    ch.qos.logback
    logback-classic
    1.2.2
    com.fasterxml.jackson.core
    jackson-databind
    2.8.7
    mysql
    mysql-connector-java
    5.1.41
    runtime
    com.mchange
    c3p0
    0.9.5.2
    org.mybatis
    mybatis
    ${mybatis.version}
    org.mybatis
    mybatis-spring
    1.3.1
    org.springframework
    spring-core
    ${spring.version}
    org.springframework
    spring-beans
    ${spring.version}
    org.springframework
    spring-context
    ${spring.version}
    org.springframework
    spring-jdbc
    ${spring.version}
    org.springframework
    spring-tx
    ${spring.version}
    org.springframework
    spring-web
    ${spring.version}
    org.springframework
    spring-webmvc
    ${spring.version}
    org.springframework
    spring-test
    ${spring.version}
  2. spring配置文件,主要是dao、service、controller

    1. spring-dao.xml

      jdbc.properties

      jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8jdbc.username=rootjdbc.password=root

      提示:配置文件中的jdbc.username,如果写成username,可能会与系统环境中的username变量冲突,所以到时候真正连接数据库的时候,用户名就被替换成系统中的用户名(有得可能是administrator),那肯定是连接不成功的

      mybatis-config.xml

    2. spring-service.xml

    3. spring-controller.xml

  3. web.xml

    dispatcherServlet
    org.springframework.web.servlet.DispatcherServlet
    contextConfigLocation
    classpath:spring/spring-*.xml
    1
    dispatcherServlet
    /
  4. logback.xml(日志)

    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
  5. generatorConfig.xml,mybatis逆向工程,生成dao(即mapper)和model(即po),这里先不用

    clipboard.png

    到这里基本的配置都完了,剩下的就是写代码。

3、代码,应用实例(图书管理系统)

mysql数据文件:

SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for appointment-- ----------------------------DROP TABLE IF EXISTS `appointment`;CREATE TABLE `appointment` (  `book_id` bigint(20) NOT NULL COMMENT '图书ID',  `student_id` bigint(20) NOT NULL COMMENT '学号',  `appoint_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '预约时间',  PRIMARY KEY (`book_id`,`student_id`),  KEY `idx_appoint_time` (`appoint_time`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='预约图书表';-- ------------------------------ Records of appointment-- ----------------------------INSERT INTO `appointment` VALUES ('1000', '12345678910', '2018-01-16 15:44:36');INSERT INTO `appointment` VALUES ('1001', '12345678910', '2018-01-16 15:58:02');-- ------------------------------ Table structure for book-- ----------------------------DROP TABLE IF EXISTS `book`;CREATE TABLE `book` (  `book_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID',  `name` varchar(100) NOT NULL COMMENT '图书名称',  `number` int(11) NOT NULL COMMENT '馆藏数量',  PRIMARY KEY (`book_id`)) ENGINE=InnoDB AUTO_INCREMENT=1004 DEFAULT CHARSET=utf8 COMMENT='图书表';-- ------------------------------ Records of book-- ----------------------------INSERT INTO `book` VALUES ('1000', 'Java程序设计', '9');INSERT INTO `book` VALUES ('1001', '数据结构', '9');INSERT INTO `book` VALUES ('1002', '设计模式', '10');INSERT INTO `book` VALUES ('1003', '编译原理', '10');

3.1 dao层

在entity包中添加图书实体Book.java和预约图书实体Appointment.java

Book.java

public class Book {    private long bookId;// 图书ID    private String name;// 图书名称    private int number;// 馆藏数量    // 省略构造方法,getter和setter方法,toString方法}

Appointment.java

/**预约图书实体*/public class Appointment {    private long bookId;// 图书ID    private long studentId;// 学号    private Date appointTime;// 预约时间    // 多对一的复合属性    private Book book;// 图书实体        // 省略构造方法,getter和setter方法,toString方法}

在dao包新建接口BookDao.java和Appointment.java

BookDao.java

public interface BookDao {    /**     * 通过ID查询单本图书     *     * @param id     * @return     */    Book queryById(long id);    /**     * 查询所有图书     *     * @param offset 查询起始位置     * @param limit 查询条数     * @return     */    List
queryAll(@Param("offset") int offset, @Param("limit") int limit); /** * 减少馆藏数量 * * @param bookId * @return 如果影响行数等于>1,表示更新的记录行数 */ int reduceNumber(long bookId);}

AppointmentDao.java

public interface AppointmentDao {    /**     * 插入预约图书记录     *     * @param bookId     * @param studentId     * @return 插入的行数     */    int insertAppointment(@Param("bookId") long bookId, @Param("studentId") long studentId);    /**     * 通过主键查询预约图书记录,并且携带图书实体     *     * @param bookId     * @param studentId     * @return     */    Appointment queryByKeyWithBook(@Param("bookId") long bookId, @Param("studentId") long studentId);}

提示:这里为什么要给方法的参数添加@Param注解呢?是因为该方法有两个或以上的参数,一定要加,不然mybatis识别不了。上面的BookDao接口的queryById方法和reduceNumber方法只有一个参数book_id,所以可以不用加 @Param注解,当然加了也无所谓~

我们不需要实现dao接口, mybatis会动态实现,但是我们需要编写相应的mapper。在mapper目录里新建两个文件BookDao.xml和ppointmentDao.xml,分别对应上面两个dao接口。

BookDao.xml

UPDATE book SET number = number - 1 WHERE book_id = #{bookId} AND number > 0

AppointmentDao.xml

INSERT ignore INTO appointment (book_id, student_id) VALUES (#{bookId}, #{studentId})

mapper总结:namespace是该xml对应的接口全名,select和update中的id对应方法名,resultType是返回值类型,parameterType是参数类型(这个其实可选),最后#{...}中填写的是方法的参数,还有一个小技巧要交给大家,就是在返回Appointment对象包含了一个属性名为book的Book对象,那么可以使用"book.属性名"的方式来取值,看上面queryByKeyWithBook方法的sql。

测试:在测试分支建立相应的包,测试前需要让程序读入spring-dao和mybatis等配置文件,所以我这里就抽离出来一个BaseTest类,只要是测试方法就继承它即可。

BaseTest.java

/*** 配置spring和junit整合,junit启动时加载springIOC容器 spring-test,junit*/@RunWith(SpringJUnit4ClassRunner.class)// 告诉junit spring配置文件@ContextConfiguration({ "classpath:spring/spring-dao.xml", "classpath:spring/spring-service.xml" })public class BaseTest {}

新建BookDaoTest.java和AppointmentDaoTest.java两个dao测试文件

BookDaoTest.java

public class BookDaoTest extends BaseTest {    @Autowired    private BookDao bookDao;    @Test    public void testQueryById() throws Exception {        long bookId = 1000;        Book book = bookDao.queryById(bookId);        System.out.println(book);    }    @Test    public void testQueryAll() throws Exception {        List
books = bookDao.queryAll(0, 4); for (Book book : books) { System.out.println(book); } } @Test public void testReduceNumber() throws Exception { long bookId = 1000; int update = bookDao.reduceNumber(bookId); System.out.println("update=" + update); }}

BookDaoTest测试结果,testQueryById

clipboard.png

testQueryAll

clipboard.png

testReduceNumber

clipboard.png

AppointmentDaoTest.java

public class AppointmentDaoTest extends BaseTest{    @Autowired    private AppointmentDao appointmentDao;    @Test    public void testInsertAppointment() throws Exception {        long bookId = 1000;        long studentId = 12345678910L;        int insert = appointmentDao.insertAppointment(bookId, studentId);        System.out.println("insert=" + insert);    }    @Test    public void testQueryByKeyWithBook() throws Exception {        long bookId = 1000;        long studentId = 12345678910L;        Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId);        System.out.println(appointment);        System.out.println(appointment.getBook());    }}

AppointmentDaoTest测试结果,testInsertAppointment

clipboard.png

testQueryByKeyWithBook

clipboard.png

3.2 servic层

我们先定义几个预约图书操作返回码的数据字典,也就是我们要返回给客户端的信息。我们这类使用枚举类

返回码 说明
1 预约成功
0 库存不足
-1 重复预约
-2 系统异常

AppointStateEnum.java

package com.messchx.ssm.enums;/** * 使用枚举表述常量数据字典 */public enum AppointStateEnum {    SUCCESS(1, "预约成功"), NO_NUMBER(0, "库存不足"), REPEAT_APPOINT(-1, "重复预约"), INNER_ERROR(-2, "系统异常");    private int state;    private String stateInfo;    private AppointStateEnum(int state, String stateInfo) {        this.state = state;        this.stateInfo = stateInfo;    }    public int getState() {        return state;    }    public String getStateInfo() {        return stateInfo;    }    public static AppointStateEnum stateOf(int index) {        for (AppointStateEnum state : values()) {            if (state.getState() == index) {                return state;            }        }        return null;    }}

在dto包下新建AppointExecution.java用来存储我们执行预约操作的返回结果。

AppointExecution.java

/*** 封装预约执行后结果*/public class AppointExecution {    // 图书ID    private long bookId;    // 秒杀预约结果状态    private int state;    // 状态标识    private String stateInfo;    // 预约成功对象    private Appointment appointment;    public AppointExecution() {    }    // 预约失败的构造器    public AppointExecution(long bookId, AppointStateEnum stateEnum) {        this.bookId = bookId;        this.state = stateEnum.getState();        this.stateInfo = stateEnum.getStateInfo();    }    // 预约成功的构造器    public AppointExecution(long bookId, AppointStateEnum stateEnum, Appointment appointment) {        this.bookId = bookId;        this.state = stateEnum.getState();        this.stateInfo = stateEnum.getStateInfo();        this.appointment = appointment;    }        // 省略getter和setter方法,toString方法}

在exception包下新建三个文件NoNumberException.java RepeatAppointException.java AppointException.java预约业务异常类(都需要继承RuntimeException),分别是无库存异常、重复预约异常、预约未知错误异常,用于业务层非成功情况下的返回(即成功返回结果,失败抛出异常)。

NoNumberException.java

/*** 库存不足异常*/public class NoNumberException extends RuntimeException {    public NoNumberException(String message) {        super(message);    }    public NoNumberException(String message, Throwable cause) {        super(message, cause);    }}

RepeatAppointException.java

/*** 重复预约异常*/public class RepeatAppointException extends RuntimeException {    public RepeatAppointException(String message) {        super(message);    }    public RepeatAppointException(String message, Throwable cause) {        super(message, cause);    }}

AppointException.java

/*** 预约业务异常*/public class AppointException extends RuntimeException {    public AppointException(String message) {        super(message);    }    public AppointException(String message, Throwable cause) {        super(message, cause);    }}

在service包下新建BookService.java图书业务接口

BookService.java

/*** 业务接口:站在"使用者"角度设计接口 三个方面:方法定义粒度,参数,返回类型(return 类型/异常)*/public interface BookService {    /**     * 查询一本图书     *     * @param bookId     * @return     */    Book getById(long bookId);    /**     * 查询所有图书     *     * @return     */    List
getList(); /** * 预约图书 * * @param bookId * @param studentId * @return */ AppointExecution appoint(long bookId, long studentId);}

在service.impl包下新建BookServiceImpl.java使用BookService接口,并实现里面的方法。

BookServiceImpl

@Servicepublic class BookServiceImpl implements BookService {    private Logger logger = LoggerFactory.getLogger(this.getClass());    // 注入Service依赖    @Autowired    private BookDao bookDao;    @Autowired    private AppointmentDao appointmentDao;    @Override    public Book getById(long bookId) {        return bookDao.queryById(bookId);    }    @Override    public List
getList() { return bookDao.queryAll(0, 1000); } @Override @Transactional /** * 使用注解控制事务方法的优点: 1.开发团队达成一致约定,明确标注事务方法的编程风格 * 2.保证事务方法的执行时间尽可能短,不要穿插其他网络操作,RPC/HTTP请求或者剥离到事务方法外部 * 3.不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制 */ public AppointExecution appoint(long bookId, long studentId) { try { // 减库存 int update = bookDao.reduceNumber(bookId); if (update <= 0) {// 库存不足 //return new AppointExecution(bookId, AppointStateEnum.NO_NUMBER);//错误写法 throw new NoNumberException("no number"); } else { // 执行预约操作 int insert = appointmentDao.insertAppointment(bookId, studentId); if (insert <= 0) {// 重复预约 //return new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT);//错误写法 throw new RepeatAppointException("repeat appoint"); } else {// 预约成功 Appointment appointment = appointmentDao.queryByKeyWithBook(bookId, studentId); return new AppointExecution(bookId, AppointStateEnum.SUCCESS, appointment); } } // 要先于catch Exception异常前先catch住再抛出,不然自定义的异常也会被转换为AppointException,导致控制层无法具体识别是哪个异常 } catch (NoNumberException e1) { throw e1; } catch (RepeatAppointException e2) { throw e2; } catch (Exception e) { logger.error(e.getMessage(), e); // 所有编译期异常转换为运行期异常 //return new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);//错误写法 throw new AppointException("appoint inner error:" + e.getMessage()); } }}

测试一下业务代码

BookServiceImplTest.java

public class BookServiceImplTest extends BaseTest {    @Autowired    private BookService bookService;    @Test    public void testAppoint() throws Exception {        long bookId = 1001;        long studentId = 12345678910L;        AppointExecution execution = bookService.appoint(bookId, studentId);        System.out.println(execution);    }}

BookServiceImplTest测试结果

clipboard.png
首次执行是“预约成功”,如果再次执行的话,应该会出现“重复预约”

在dto包里新建一个封装json返回结果的类Result.java,设计成泛型

Result.java

/*** 封装json对象,所有返回结果都使用它*/public class Result
{ private boolean success;// 是否成功标志 private T data;// 成功时返回的数据 private String error;// 错误信息 public Result() { } // 成功时的构造器 public Result(boolean success, T data) { this.success = success; this.data = data; } // 错误时的构造器 public Result(boolean success, String error) { this.success = success; this.error = error; } // 省略getter和setter方法}

3.3 controller层

BookController.java

@Controller@RequestMapping("/book") // url:/模块/资源/{id}/细分 /seckill/listpublic class BookController {    private Logger logger = LoggerFactory.getLogger(this.getClass());    @Autowired    private BookService bookService;    @RequestMapping(value = "/list", method = RequestMethod.GET)    private String list(Model model) {        List
list = bookService.getList(); model.addAttribute("list", list); // list.jsp + model = ModelAndView return "list";// WEB-INF/jsp/"list".jsp } @RequestMapping(value = "/{bookId}/detail", method = RequestMethod.GET) private String detail(@PathVariable("bookId") Long bookId, Model model) { if (bookId == null) { return "redirect:/book/list"; } Book book = bookService.getById(bookId); if (book == null) { return "forward:/book/list"; } model.addAttribute("book", book); return "detail"; } //ajax json @RequestMapping(value = "/{bookId}/appoint", method = RequestMethod.POST, produces = { "application/json; charset=utf-8" }) @ResponseBody private Result
appoint(@PathVariable("bookId") Long bookId, @RequestParam("studentId") Long studentId) { if (studentId == null || studentId.equals("")) { return new Result<>(false, "学号不能为空"); } //AppointExecution execution = bookService.appoint(bookId, studentId);//错误写法,不能统一返回,要处理异常(失败)情况 AppointExecution execution = null; try { execution = bookService.appoint(bookId, studentId); } catch (NoNumberException e1) { execution = new AppointExecution(bookId, AppointStateEnum.NO_NUMBER); } catch (RepeatAppointException e2) { execution = new AppointExecution(bookId, AppointStateEnum.REPEAT_APPOINT); } catch (Exception e) { execution = new AppointExecution(bookId, AppointStateEnum.INNER_ERROR); } return new Result
(true, execution); }}

转载地址:http://eaixa.baihongyu.com/

你可能感兴趣的文章
打造一个上传图片到图床利器的插件(Mac版 开源)
查看>>
iOS横竖屏
查看>>
thinkphp判断更新是否成功
查看>>
Do While ... Loop 与 Do Until ... Loop 的区别
查看>>
【Linux】查询某个字符串出现次数
查看>>
高效使用jquery之一:请使用'On'函数
查看>>
冲刺第一周第三天
查看>>
ERP环境检测工具设计与实现 Environment Detection
查看>>
不要在构造中做太多事情,不然有时候会出现有意思的代码~
查看>>
IIS 发布网站遇到的问题
查看>>
NuGet学习笔记(2)——使用图形化界面打包自己的类库
查看>>
xcode中没有autoSizing的设置
查看>>
字符编码
查看>>
企业应用:应用层查询接口设计
查看>>
浅谈Excel开发:十 Excel 开发中与线程相关的若干问题
查看>>
nfd指令的详细说明
查看>>
安装VisualSvn Server时遇到的问题
查看>>
不用Visual Studio,5分钟轻松实现一张报表
查看>>
人脸识别 开放书籍 下载地址
查看>>
Notepad++配置Python开发环境
查看>>