资讯专栏INFORMATION COLUMN

SSM框架整合

lanffy / 1610人阅读

摘要:环境工具数据库连接工具准备工作步骤新建项目建好基本目录,按住配置目录,如下图新建几个包,如下图包名名称作用数据访问层接口与数据操作有关的都放在这里实体类一般与数据库的表相对应,封装层取出来的数据为一个对象,也就是我们常说的,一般只在层与层之

环境

Windows10

JDK1.8

MySql5.5

工具

Maven3.5

IDEA2017

Navicat(数据库连接工具)

准备工作 步骤 1、新建Maven webapp项目



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

新建几个包,如下图:

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

配置完成的目录如下:

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}
    
  

spring配置文件,主要是dao、service、controller

spring-dao.xml



    
    
    

    
    
        
        
        
        
        

        
        
        
        
        
        
        
        
        
    

    
    
        
        
        
        
        
        
        
        
    

    
    
        
        
        
        
    
    

jdbc.properties

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

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

mybatis-config.xml




    
    
        
        

        
        

        
        
    

spring-service.xml



    
    

    
    
        
        
    

    
    

spring-controller.xml



    
    
    
    

    
    

    
    
        
        
        
    

    
    

web.xml


  
  
  
    dispatcherServlet
    org.springframework.web.servlet.DispatcherServlet
    
    
      contextConfigLocation
      classpath:spring/spring-*.xml
    
    1
  
  
    dispatcherServlet
    
    /
    

logback.xml(日志)



    
        
        
            %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
        
    

    
        
    

generatorConfig.xml,mybatis逆向工程,生成dao(即mapper)和model(即po),这里先不用





    
    

    
    

    

        
        
            
            
        

        
        
        


        
        
            
        


        
        

            
            
            
            
            
            
            
            
        

        
        
            
        

        
        
            
        

        
        

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

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

testQueryAll

testReduceNumber

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

testQueryByKeyWithBook

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

@Service
public 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测试结果

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

在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/list
public 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);
    }

}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68261.html

相关文章

  • 从零开始搭建SSM框架(Spring + Spring MVC + Mybatis)

    摘要:打开,,选中,然后再选中,输入项目的和,指定等配置,修改,打开项目,添加一些必要的目录,最终项目框架目录图如下修改文件,指定各依赖和插件的版本等信息在标签里面管理各依赖的版本号添加项目依赖管理依赖配置好之后,开始整合。 最近在回顾和总结一些技术,想到了把之前比较火的 SSM 框架重新搭建出来,作为一个小结,同时也希望本文章写出来能对大家有一些帮助和启发,因本人水平有限,难免可能会有一些...

    MiracleWong 评论0 收藏0
  • Maven多模块项目搭建+整合SSM框架

    摘要:继承作用就是避免配置重复,对于子项目来说应该关心父项目是怎么样配置的。聚合字面理解就是聚在一起合作完成工作,就是将子模块聚集起来完成相应的项目需求父工程的搭建项目结构在父工程中,主要负责完成依赖的版本管理,并不是实际的依赖。 从大二开始就一直关注segmentFault,在问题专区帮忙回答一些自己知晓的问题;在写这篇文章之前我一直会在朋友圈发一些自己遇到的问题以及解决办法,这是第一次写...

    liaosilzu2007 评论0 收藏0
  • SSM框架整合

    摘要:整合项目结构导入版本号相关包相关包相关包相关包数据库连接池集成标准标签库日志相关包单元测试相关包里面为空开发环境下,日志级别设置 ssm整合项目结构 showImg(https://segmentfault.com/img/bVbsw8O?w=533&h=815); Maven导入jar pom.xml 4.0.0 cn.scitc Test 1...

    twohappy 评论0 收藏0
  • ssm框架整合

    ssm整合 开发环境ide,mysql数据库,Spring+SpringMVC+Mybatis,tomcat8.5,jdk使用的是1.7版本。 第一步:导入jar包 Spring+ SpringMVC + MyBatis + Mybatis-spring整合包 AOP联盟+织入 + c3p0 数据库连接池 + MySQL连接驱动 + jstl 链接:https://pan.baidu.com/...

    Simon_Zhou 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<