前言

如果看过前几篇文章,对 SpringMyBatis 有了一定了解,一定想上手试试。这篇文章从 0 到 1,手把手整合 SSM (Spring、Spring MVC、MyBatis)。

本篇是代码篇,在 PC 端浏览更佳,源码在文末

文章目录

    • 前言
    • 搭建整合 SSM 之 HelloWorld
      • 开发环境
      • 目录
      • 环境搭建
        • 新建项目
        • 导入依赖
      • 编码
        • 配置文件
          • spring-dao.xml
          • jdbc.properties
          • mybatis-config.xml
          • spring-service.xml
          • spring-web.xml
          • web.xml
          • 日志
          • 配置说明
        • SSM实例-图书管理系统
          • sql
          • 实体
          • dao接口
          • dao接口xml
          • dao接口测试
          • 业务层-结果集封装
          • Service 业务接口代码
          • web层
          • 运行项目
      • 最后:知识点总结

搭建整合 SSM 之 HelloWorld

开发环境

  • idea
  • MySql5.x
  • jdk8
  • maven

对应的技术入门在 公众号 历史文章 都可以找到

目录

目录包括 main、resources、mapper、webapp,不一一介绍,不熟悉查看前面文章。

环境搭建

新建项目

这里使用的是 IDEA 编辑器,新建一个 Maven 工程,选择 web 项目。

导入依赖

使用 Maven 管理项目 jar ,只需要在 pom.xml 加如相关依赖即可。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>javapub.rodert.github</groupId><artifactId>ssm_helloword_web</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><url></url>
<!--    源码:https://github.com/Rodert/JavaPub--><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.version>4.3.6.RELEASE</spring.version></properties><dependencies><!-- 单元测试 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!-- 1.日志 --><!-- 实现slf4j接口并整合 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.1</version></dependency><!-- 2.数据库 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.37</version><scope>runtime</scope></dependency><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><!-- DAO: MyBatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.3.0</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.2.3</version></dependency><!-- 3.Servlet web --><dependency><groupId>taglibs</groupId><artifactId>standard</artifactId><version>1.1.2</version></dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.5.4</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency><!-- 4.Spring --><!-- 1)Spring核心 --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><!-- 2)Spring DAO层 --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><!-- 3)Spring web --><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><!-- 4)Spring test --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><!-- redis客户端:Jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.7.3</version></dependency><dependency><groupId>com.dyuproject.protostuff</groupId><artifactId>protostuff-core</artifactId><version>1.0.8</version></dependency><dependency><groupId>com.dyuproject.protostuff</groupId><artifactId>protostuff-runtime</artifactId><version>1.0.8</version></dependency><!-- Map工具类 --><dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2</version></dependency><!--注解--><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency></dependencies><build><finalName>ssm_helloword_web</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>7</source><target>7</target></configuration></plugin></plugins></build></project>
<!--    源码:https://github.com/Rodert/JavaPub-->

编码

配置文件

spring-dao.xml
  • 先在spring文件夹里新建spring-dao.xml文件,我们这里分三层,分别是dao service web。
  1. 加载数据库配置
  2. 配置数据库连接池
  3. 配置 SqlSessionFactory 对象(MyBatis)
  4. 配置扫描 dao 层接口,动态代理实现 Dao 实现类,执行 sql 写在 xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!-- 配置整合mybatis过程 --><!-- 1.配置数据库相关参数properties的属性:${url} --><context:property-placeholder location="classpath:jdbc.properties" /><!-- 2.数据库连接池 --><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}" /><!-- c3p0连接池的私有属性 --><property name="maxPoolSize" value="30" /><property name="minPoolSize" value="10" /><!-- 关闭连接后不自动commit --><property name="autoCommitOnClose" value="false" /><!-- 获取连接超时时间 --><property name="checkoutTimeout" value="10000" /><!-- 当获取连接失败重试次数 --><property name="acquireRetryAttempts" value="2" /></bean><!-- 3.配置SqlSessionFactory对象 --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!-- 注入数据库连接池 --><property name="dataSource" ref="dataSource" /><!-- 配置MyBaties全局配置文件:mybatis-config.xml --><property name="configLocation" value="classpath:Mybatis-config.xml" /><!-- 扫描entity包 使用别名 --><property name="typeAliasesPackage" value="javapub.rodert.github.entity" /><!-- 扫描sql配置文件:mapper需要的xml文件 --><property name="mapperLocations" value="classpath:mapper/*.xml" /></bean><!-- 4.配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中 --><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!-- 注入sqlSessionFactory --><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /><!-- 给出需要扫描Dao接口包 --><property name="basePackage" value="javapub.rodert.github.dao" /></bean>
</beans>
jdbc.properties

数据库配置,在 resources 文件夹里新建一个 jdbc.properties 文件,注意自己的密码。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm1?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=
mybatis-config.xml

MyBatis 核心文件,在recources文件夹里新建mybatis-config.xml文件。

  1. 使用自增主键
  2. 使用列别名
  3. 开启驼峰命名转换 create_time -> createTime
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 配置全局属性 --><settings><!-- 使用jdbc的getGeneratedKeys获取数据库自增主键值 --><setting name="useGeneratedKeys" value="true" /><!-- 使用列别名替换列名 默认:true --><setting name="useColumnLabel" value="true" /><!-- 开启驼峰命名转换:Table{create_time} -> Entity{createTime} --><setting name="mapUnderscoreToCamelCase" value="true" /></settings>
</configuration>
spring-service.xml

在 spring 文件夹里新建 spring-service.xml 文件。

  1. 扫描 service 包所有注解 @Service
  2. 配置事务管理器,把事务管理交由 spring 来完成
  3. 基于注解的 声明式事务,可以直接在方法上 @Transaction
<?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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 扫描service包下所有使用注解的类型 --><context:component-scan base-package="javapub.rodert.github.service" /><!-- 配置事务管理器 --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 注入数据库连接池 --><property name="dataSource" ref="dataSource" /></bean><!-- 配置基于注解的声明式事务 --><tx:annotation-driven transaction-manager="transactionManager" />
</beans>
spring-web.xml

web 层,在 spring 文件夹里新建 spring-web.xml 文件。

  1. 开启SpringMVC注解模式,可以使用@RequestMapping,@PathVariable,@ResponseBody等
  2. 对静态资源处理,如js,css,jpg等
  3. 配置jsp 显示ViewResolver,及渲染后的 JSP
  4. 扫描web层 @Controller
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"><!-- 配置SpringMVC --><!-- 1.开启SpringMVC注解模式 --><!-- 简化配置:(1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter(2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持--><mvc:annotation-driven /><!-- 2.静态资源默认servlet配置(1)加入对静态资源的处理:js,gif,png(2)允许使用"/"做整体映射--><mvc:default-servlet-handler/><!-- 3.配置jsp 显示ViewResolver --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<!--        <property name="contentType" value="text/html"/>--><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean><!-- 4.扫描web相关的bean --><context:component-scan base-package="javapub.rodert.github.web" />
</beans>
web.xml

修改 web.xml 文件了,它在 webapp 的 WEB-INF 下。也可以在这里配置过滤器、监听器等。

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1" metadata-complete="true"><!-- 如果是用mvn命令生成的xml,需要修改servlet版本为3.1 --><!-- 配置DispatcherServlet --><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 配置springMVC需要加载的配置文件spring-dao.xml,spring-service.xml,spring-web.xmlMybatis - > spring -> springmvc--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/spring-*.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><!-- 默认匹配所有的请求 --><url-pattern>/</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list>
</web-app>
日志

配置一些简单的日志,使用 logback ,在 resources 文件夹里新建logback.xml 文件。

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true"><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><!-- encoders are by default assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder --><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><root level="debug"><appender-ref ref="STDOUT" /></root>
</configuration>
配置说明

以上配置是整合 SSM 的基础配置,目录结构如图所示:


SSM实例-图书管理系统

sql

以上部分整个 SSM 框架就已经搭建好了,下面是一个 Demo ,供参考。

新建俩张表,图书表 book 和 预约图书表 appointment,并初始化数据。

/*
Navicat MySQL Data TransferSource Server         : localhost
Source Server Version : 50716
Source Host           : localhost:3306
Source Database       : ssm1Target Server Type    : MYSQL
Target Server Version : 50716
File Encoding         : 65001Date: 2020-07-12 16:50:43
*/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
-- ------------------------------ ----------------------------
-- 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 ('2000', 'Java程序设计', '20');
INSERT INTO `book` VALUES ('2001', '数据结构', '7');
INSERT INTO `book` VALUES ('2002', '设计模式', '20');
INSERT INTO `book` VALUES ('2003', '编译原理', '20');
实体

entity 包下新建实体 BookAppointment

  • Book.java
package javapub.rodert.github.entity;import lombok.Getter;
import lombok.Setter;
import lombok.ToString;/*** @author wangshiyu rodert* @date 2020/7/6 20:58* @description** @Data 注解,简化代码,自动添加get set toSting 方法* @Getter* @Setter* @ToString*/
@Getter
@Setter
@ToString
public class Book {private long bookId;// 图书IDprivate String name;// 图书名称private int number;// 馆藏数量// 省略构造方法,getter和setter方法,toString方法}
  • Appointment.java
package javapub.rodert.github.entity;/*** @author wangshiyu rodert* @date 2020/7/6 20:58* @description*/import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.util.Date;/*** 预约图书实体* @Data 注解,简化代码,自动添加get set toSting 方法*/
@Data
public class Appointment {private long bookId;// 图书IDprivate long studentId;// 学号private Date appointTime;// 预约时间// 多对一的复合属性private Book book;// 图书实体// 省略构造方法,getter和setter方法,toString方法}
dao接口

dao包新建接口 BookDao.javaAppointment.java

  • BookDao.java
package javapub.rodert.github.dao;/*** @author wangshiyu rodert* @date 2020/7/6 21:01* @description*/import javapub.rodert.github.entity.Book;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface BookDao {/*** 通过ID查询单本图书** @param id* @return*/Book queryById(long id);/*** 查询所有图书** @param offset 查询起始位置* @param limit 查询条数* @return*/List<Book> queryAll(@Param("offset") int offset, @Param("limit") int limit);/*** 减少馆藏数量** @param bookId* @return 如果影响行数等于>1,表示更新的记录行数*/int reduceNumber(long bookId);
}
  • AppointmentDao.java
package javapub.rodert.github.dao;/*** @author wangshiyu rodert* @date 2020/7/6 21:01* @description*/import javapub.rodert.github.entity.Appointment;
import org.apache.ibatis.annotations.Param;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接口xml

这里不需要写 dao接口 的实现类,mybatis会帮我们动态实现,上面我们已经在 spring-dao.xml 配置了动态扫描。现在需要编写相应的 mapper
mapper 目录里新建两个文件 BookDao.xmlAppointmentDao.xml ,分别对应上面两个dao接口。

  • BookDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="javapub.rodert.github.dao.BookDao"><!-- 目的:为dao接口方法提供sql语句配置 --><select id="queryById" resultType="Book" parameterType="long"><!-- 具体的sql -->SELECTbook_id,name,numberFROMbookWHEREbook_id = #{bookId}</select><select id="queryAll" resultType="Book">SELECTbook_id,name,numberFROMbookORDER BYbook_idLIMIT #{offset}, #{limit}</select><update id="reduceNumber">UPDATE bookSET number = number - 1WHEREbook_id = #{bookId}AND number > 0</update>
</mapper>
  • AppointmentDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="javapub.rodert.github.dao.AppointmentDao"><insert id="insertAppointment"><!-- ignore 主键冲突,报错 -->INSERT ignore INTO appointment (book_id, student_id)VALUES (#{bookId}, #{studentId})</insert><select id="queryByKeyWithBook" resultType="Appointment"><!-- 如何告诉MyBatis把结果映射到Appointment同时映射book属性 --><!-- 可以自由控制SQL -->SELECTa.book_id,a.student_id,a.appoint_time,b.book_id "book.book_id",b.`name` "book.name",b.number "book.number"FROMappointment aINNER JOIN book b ON a.book_id = b.book_idWHEREa.book_id = #{bookId}AND a.student_id = #{studentId}</select>
</mapper>

mapper 说明namespace 是 xml 对应的接口全名,selectupdate 中的 id 对应方法名(唯一),resultType 是返回值类型,parameterType 是参数类型(这个其实可选),#{...} 中填写的是方法的参数


dao接口测试

现在的写法是从数据库层向前(web)写,现在测试一下 dao 接口,编写测试类。

因为每次测试都要加载配置文件,所有抽离一个类(BaseTest),每次测试方法都继承它。

  • BaseTest.java
package javapub.rodert.github;/*** @author wangshiyu rodert* @date 2020/7/6 21:07* @description*/import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/*** 配置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.javaAppointmentDaoTest.java 两个 dao 测试文件。

  • BookDao.java
package javapub.rodert.github.dao;/*** @author wangshiyu rodert* @date 2020/7/6 21:08* @description*/import javapub.rodert.github.BaseTest;
import javapub.rodert.github.entity.Book;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;import java.util.List;public class BookDaoTest extends BaseTest {@Autowiredprivate BookDao bookDao;@Testpublic void testQueryById() throws Exception {long bookId = 1000;Book book = bookDao.queryById(bookId);System.out.println(book);}@Testpublic void testQueryAll() throws Exception {List<Book> books = bookDao.queryAll(0, 4);for (Book book : books) {System.out.println(book);}}@Testpublic void testReduceNumber() throws Exception {long bookId = 1000;int update = bookDao.reduceNumber(bookId);System.out.println("update=" + update);}}
  • AppointmentDaoTest.java
package javapub.rodert.github.dao;/*** @author wangshiyu rodert* @date 2020/7/6 21:18* @description*/import javapub.rodert.github.BaseTest;
import javapub.rodert.github.entity.Appointment;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;public class AppointmentDaoTest extends BaseTest {@Autowiredprivate AppointmentDao appointmentDao;@Testpublic void testInsertAppointment() throws Exception {long bookId = 1000;long studentId = 12345678910L;int insert = appointmentDao.insertAppointment(bookId, studentId);System.out.println("insert=" + insert);}@Testpublic 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());}}
  • BookDaoTest.java – > testQueryById()

测试方法都验证过,没有问题,不一一测试了


业务层-结果集封装

到这里,我们的 dao 层,及数据库接口操作都没有问题,下面开始业务层编写。

如果你有实战项目经验,那一定会发现,对于后端接口,我们都会定义一个统一的返回格式,及定义一个返回标准方便前端解析,如下:

{"code":200,"message":"成功","result":{},"isSuccess":true
}

开始写我们的代码,新建枚举类,用来定义预约业务的数据字典。如果不太明白,先看代码,后面在 JavaPub 微信公众号文章索引中查找对应文章。

新建一个包叫 enums,在里面新建一个枚举类 AppointStateEnum.java

  • AppointStateEnum.java
package javapub.rodert.github.enums;/*** @author wangshiyu rodert* @date 2020/7/6 21:20* @description*//*** 使用枚举表述常量数据字典*/
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
package javapub.rodert.github.dto;/*** @author wangshiyu rodert* @date 2020/7/7 16:26* @description*/import javapub.rodert.github.entity.Appointment;
import javapub.rodert.github.enums.AppointStateEnum;
import lombok.Data;/*** 封装预约执行后结果*/
@Data
public class AppointExecution {// 图书IDprivate 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 —运行时异常类),分别是无库存异常、重复预约异常、预约未知错误异常,用于业务层非成功情况下的返回(即成功返回结果,失败抛出异常)。为事务做准备。

  • AppointException.java
package javapub.rodert.github.exception;/*** @author wangshiyu rodert* @date 2020/7/7 16:31* @description*//*** 预约业务异常*/
public class AppointException extends RuntimeException {public AppointException(String message) {super(message);}public AppointException(String message, Throwable cause) {super(message, cause);}}
  • NoNumberException.java
/*** @author wangshiyu rodert* @date 2020/7/7 16:30* @description*/
package javapub.rodert.github.exception;/*** 库存不足异常*/
public class NoNumberException extends RuntimeException {public NoNumberException(String message) {super(message);}public NoNumberException(String message, Throwable cause) {super(message, cause);}}
  • RepeatAppointException.java

/*** @author wangshiyu rodert* @date 2020/7/7 16:31* @description*/
package javapub.rodert.github.exception;/*** 重复预约异常*/
public class RepeatAppointException extends RuntimeException {public RepeatAppointException(String message) {super(message);}public RepeatAppointException(String message, Throwable cause) {super(message, cause);}}
Service 业务接口代码

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

  • BookService.java

/*** @author wangshiyu rodert* @date 2020/7/7 16:32* @description*/
package javapub.rodert.github.service;import javapub.rodert.github.dto.AppointExecution;
import javapub.rodert.github.entity.Book;import java.util.List;/*** 业务接口:站在"使用者"角度设计接口 三个方面:方法定义粒度,参数,返回类型(return 类型/异常)*/
public interface BookService {/*** 查询一本图书** @param bookId* @return*/Book getById(long bookId);/*** 查询所有图书** @return*/List<Book> getList();/*** 预约图书** @param bookId* @param studentId* @return*/AppointExecution appoint(long bookId, long studentId);}

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

  • BookServiceImpl.java
/*** @author wangshiyu rodert* @date 2020/7/7 16:39* @description*/
package javapub.rodert.github.service.impl;import javapub.rodert.github.dao.AppointmentDao;
import javapub.rodert.github.dao.BookDao;
import javapub.rodert.github.dto.AppointExecution;
import javapub.rodert.github.entity.Appointment;
import javapub.rodert.github.entity.Book;
import javapub.rodert.github.enums.AppointStateEnum;
import javapub.rodert.github.exception.AppointException;
import javapub.rodert.github.exception.NoNumberException;
import javapub.rodert.github.exception.RepeatAppointException;
import javapub.rodert.github.service.BookService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
public class BookServiceImpl implements BookService {private Logger logger = LoggerFactory.getLogger(this.getClass());// 注入Service依赖@Autowiredprivate BookDao bookDao;@Autowiredprivate AppointmentDao appointmentDao;@Overridepublic Book getById(long bookId) {return bookDao.queryById(bookId);}@Overridepublic List<Book> 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 | RepeatAppointException e1) {throw e1;} catch (Exception e) {logger.error(e.getMessage(), e);// 所有编译期异常转换为运行期异常//return new AppointExecution(bookId, AppointStateEnum.INNER_ERROR);//错误写法throw new AppointException("appoint inner error:" + e.getMessage());}}}

实现类使用了我们上边定义的异常方法 RepeatAppointException ,用于业务层非成功情况下的返回(即成功返回结果,失败抛出异常)。触发事务。


测试一下业务层代码,这里演示预约图书业务。

  • BookServiceImplTest.java

/*** @author wangshiyu rodert* @date 2020/7/7 16:40* @description*/
package javapub.rodert.github.service.impl;import javapub.rodert.github.BaseTest;
import javapub.rodert.github.dto.AppointExecution;
import javapub.rodert.github.service.BookService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;public class BookServiceImplTest extends BaseTest {@Autowiredprivate BookService bookService;@Testpublic void testAppoint() throws Exception {long bookId = 1001;long studentId = 12345678910L;AppointExecution execution = bookService.appoint(bookId, studentId);System.out.println(execution);}}

测试结果:

首次执行是“预约成功”,如果再次执行的话,应该会出现“重复预约”,至此,我们所有的后台代码都通过单元测试啦~~ 是不是很开心~


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

  • Result.java
package javapub.rodert.github.dto;/*** @author wangshiyu rodert* @date 2020/7/7 21:00* @description*/import lombok.Data;/*** 封装json对象,所有返回结果都使用它*/
@Data
public class Result<T> {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方法 使用注解代替
}
web层

web 层,也就是 controller 层,我们在web包下新建BookController.java文件。

  • BookController.java
package javapub.rodert.github.web;/*** @author wangshiyu rodert* @date 2020/7/7 21:05* @description*/import javapub.rodert.github.dto.AppointExecution;
import javapub.rodert.github.dto.Result;
import javapub.rodert.github.entity.Book;
import javapub.rodert.github.enums.AppointStateEnum;
import javapub.rodert.github.exception.NoNumberException;
import javapub.rodert.github.exception.RepeatAppointException;
import javapub.rodert.github.service.BookService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;import java.util.List;@RestController
@RequestMapping("/book") // url:/模块/资源/{id}/细分 /seckill/list
public class BookController {private Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate BookService bookService;@RequestMapping(value = "/test")public ModelAndView test(){ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("book/test");modelAndView.addObject("key","welcome javaPub");return modelAndView;}@RequestMapping(value = "/list", method = RequestMethod.GET)private String list(Model model) {List<Book> list = bookService.getList();model.addAttribute("list", list);// list.jsp + model = ModelAndViewreturn "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//method = RequestMethod.POST,@RequestMapping(value = "/{bookId}/appoint",  produces = {"application/json; charset=utf-8" })@ResponseBodyprivate Result<AppointExecution> 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<AppointExecution>(true, execution);}}

目前大多项目都是前后端分离,我们作为服务端,一般和前端通过接口数据交互(json),像接口方法 appoint ,应该添加 @ResponseBody 注解。
测试 controller --> appoint 方法可以通过 curl ,如:

curl -H “Accept: application/json; charset=utf-8” -d “studentId=1234567890” localhost:8080/book/1003/appoint

运行项目

现在整个项目全部完成,配置tomcat,通过左上角引入 tomcat ,选择我们的项目 ssm。

启动成功后:

这里对前端代码只写较少部分,具体可参考 BookController --> book/test 接口,有需要帮助请留言。

  • BookController.java --> book/test
    @RequestMapping(value = "/test")public ModelAndView test(){ModelAndView modelAndView = new ModelAndView();modelAndView.setViewName("book/test");modelAndView.addObject("key","welcome javaPub");return modelAndView;}
  • test.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE HTML>
<html>
<head><%@ page isELIgnored="false"%><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/><title>news detail</title></head>
<body class="fn-pd24">
<h1>大家好 ${key}</h1><a href="https://mp.weixin.qq.com/s/kfyRAPnRDp8LLktjgd658Q">JavaPub知识清单</a>
</body>
</html>

通过 ModelAndView 将我们需要渲染的数据存储传输到对应视图,由 Sping MVC 定义好的视图解析器对该对象解析,最后将结果数据显示到指定页面。

完整代码地址:https://github.com/Rodert/JavaPub/code/ssm_helloworld_web/

最后:知识点总结

文章底部都有对应原创PDF,持续更新中,
教程纯手打,致力于最实用教程,微信搜:JavaPub ,无套路领取免费原创 PDF 、学习路线图,后台回复【666】。

  • 51页MyBatis
  • 19页Maven
  • 14页zookeeper
  • 10页Git
  • 18页spring
  • 8页布隆过滤器
  • 50页排序算法
  • redis系列文章
  • dubbo
  • nginx
  • ffmpeg
  • Jenkins
  • http

觉得文章内容不错,记得点赞或在看都行,这是对我最大的鼓励!

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Web | RabbitMQ | 高级应用

    Web-RabbitMQ高级目录一、消息可靠性投递1. confirm 确认模式1.1 整合Springboot/Spring后中的实现第一步:开启第二步:设置回调函数1.2 Channel类第一步:开启第二步:设置回调函数1.3 总结2. Return 退回模式第一步:开启第二步:设置回调函数小结3. 对比二、消费端确认2.1…...

    2024/4/1 4:02:35
  2. 使用VB.NET开发一个简单的串口程序

    本程序严谨程度不高,仅适合新手做成功程序使用,严谨程序后续会有更新. 这两个程序结合起来是可以用的. 单片机程序代码(用于在收到数据后做出反应) //整合自STC官方文档 #include "reg52.h" #include "intrins.h" typedef unsigned char u8; typedef unsig…...

    2024/5/9 17:51:00
  3. Java | 加密技术 | 摘要加密算法(不含原理)

    一、背景简介 1.1 含义 消息摘要采用单向Hash函数将需加密的明文"摘要"成一串密文,这一串密文亦称为数字指纹(Finger Print)。它有固定的长度,且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。这样这串摘要便可成为验证明文是否是"真…...

    2024/5/9 20:57:00
  4. 喜欢的语句记录——皮囊

    我只想着做好一点点的事情,然后期待,这么一点事,或许哪天能积累成一个不错的景观。起码是自己喜欢的景观。 每个人都已经过上不同的生活,不同的生活让许多人在这个时空里没法相处在共同的状态中,除非等彼此都老了,年迈再次抹去其他,构成我们每个人最重要的标志,或许那时…...

    2024/5/7 7:04:58
  5. 英语学习单词篇(9)

    复习:一.词根与词缀:词根与词缀 意思 example meaning1. rig 正,直 upright n. 垂直;竖立adj. 正直的,诚实的;垂直的,直立的;笔直的;合乎正道的2.host 客人;陌生人 hostile n. 敌对adj. 敌对的,敌方的;怀敌意的3.miss 送,放,派出,错过 transmission n. 传动装置…...

    2024/4/14 13:15:30
  6. 移动端不利用HTML5和echarts开发一样可以实现大数据展示及炫酷统计系统(产品技术综合)

    一、由于项目需要进行手机看板展示设计及开发展示效果图如下:上图为概况(点击相应模块进入详情页面)上图为运营统计(一些统计类图标状图折线图等......)车辆分布状况(展示在地图上分布)上图为点击一辆车的轨迹和运行情况此产品唯一亮点在于完全可以把利用HTML5和echarts…...

    2024/5/7 3:13:55
  7. 174. 地下城游戏;剑指 Offer 40. 最小的k个数;378. 有序矩阵中第K小的元素;703. 数据流中的第K大元素

    一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会…...

    2024/5/7 9:34:00
  8. spring源码系列(六)——番外篇如何编译spring的源码

    学习spring源码的第一步便是需要去编译源码;很多网友在编译spring源码的时候遇到了困难继而放弃了spring源码的学习; 这篇文章主要来阐述如何正确的编译spring源码;首先你得检查你的网线能正常上网;最好网速还行;不然你会疯掉下载spring源码 下载spring源码可以选择github…...

    2024/4/20 8:12:31
  9. 阿里面试官:小伙子,你给我说一下前后端分离的接口规范是什么?

    1. 前言 随着互联网的高速发展,前端页面的展示、交互体验越来越灵活、炫丽,响应体验也要求越来越高,后端服务的高并发、高可用、高性能、高扩展等特性的要求也愈加苛刻,从而导致前后端研发各自专注于自己擅长的领域深耕细作。 然而带来的另一个问题:前后端的对接界面双方却…...

    2024/4/20 12:45:28
  10. 制造业IT人员对MES系统的理解(业务篇/一)

    Manufacturing Execution System,简称MES,直译成中文即制造执行系统。从名字理解,MES系统可以从三个层面来理解——制造、执行、系统。定义制造对一个制造型企业/工厂来说,制造是其最基本且核心的定义。某工厂在生产某种产品的过程中,需要使用生产装置和公用能源将原辅料转…...

    2024/5/7 10:57:03
  11. Python 第8章 在硬盘上创建、读取和保存文件

    8.1文件与文件路径文件有两个关键属性:文件名、路径 文件名中,最后一个句点之后的部分称为文件的扩展名,扩展名指出了文件的类型 路径指明了文件在计算机上的位置,路径中的C:\部分是根文件夹,它包含了所有其他文件 windows中,根文件夹名为C:\,也称为C盘;OSX和Linux中,…...

    2024/5/6 20:53:06
  12. 前端基础复习-HTML要点大串烧

    1. 初识HTMLHTML 指的是超文本标记语言 (Hyper Text Markup Language)是用来描述网页的一种语言 H(很)T(甜)M(蜜)L(啦) HTML 不是一种编程语言,而是一种标记语言 (markup language) 标记语言是一套标记标签 (markup tag)所谓超文本,有2层含义:因为它可以加入图片、声…...

    2024/5/7 11:05:14
  13. Java 静态代理和两种动态代理(JDK动态代理和CGLIB动态代理)

    这里写目录标题静态代理JDK 动态代理CGLIB代理 静态代理由代理对象代理所有真实对象的功能 自己编写代理类,每个代理的功能需要单独编写 缺点:当代理功能比较多时,代理类中方法需要写很多 接口类public interface Gongneng {public void kaihui();public void faqian();}被代…...

    2024/4/14 13:16:46
  14. 1058 选择题 (20分)

    题目:批改多选题是比较麻烦的事情,本题就请你写个程序帮助老师批改多选题,并且指出哪道题错的人最多。输入格式:输入在第一行给出两个正整数 N(≤ 1000)和 M(≤ 100),分别是学生人数和多选题的个数。随后 M 行,每行顺次给出一道题的满分值(不超过 5 的正整数)、选项…...

    2024/5/7 8:01:24
  15. 4 运算符

    1.字符串 1.1 格式化字符串1.拼串 print(‘12’ + ‘3’) 2.参数传递 a = ‘12’ b = ‘3’ print(a,b) 3.占位符 s = s1 =%s,s2 = %s % (‘12’,‘3’) ’ 4.格式化的方式 a = ‘12’ b = ‘3’ s = f’{a}{b}’ 第二种: s = {}{}format(‘12’.‘3’)1.2 字符串的其他操…...

    2024/5/7 10:30:29
  16. 数学建模算法与应用学习day2——非线性规划问题

    以下内容来自司守奎编写的数学建模算法与应用学习,主要是记录自己的学习历程,转载还请标明出处! 一、非线性规划 知识点 3.1 非线性规划模型 3.1.1 非线性规划的实例与定义3.1.2 非线性规划的MATLAB解法%不完全代码(有点问题) %非线性规划的MATLAB解法 function f = fun1(…...

    2024/4/15 5:36:46
  17. 5 条件控制语句

    1. 运算符条件运算符(三元) a = 20,b = 30,print(‘a的值比较大’) if a > b else print(‘b的值比较大’) 运算符的优先级—>看表, 优先级:()>and>or>not2. 条件控制语句if语句,只会控制紧跟其后的那条语句 input()函数获取用户输入的内容,input()调用后…...

    2024/5/7 1:47:09
  18. C++学习系列三:Statements and Flow Control||Functions||Overloads and Templates||Name Visibility

    Statements and flow control A simple C++ statement is each of the individual instructions of a program, always end with a semicolon(😉, and are executed in the same order in which they appear in a program.Selection Statements: if and else The if keyword …...

    2024/4/14 13:15:40
  19. JavaWeb_07_HTML

    今日内容 1. web概念概述 2. HTMLweb概念概述 * JavaWeb:* 使用Java语言开发基于互联网的项目* 软件架构:1. C/S: Client/Server 客户端/服务器端* 在用户本地有一个客户端程序,在远程有一个服务器端程序* 如:QQ,迅雷...* 优点:1. 用户体验好* 缺点:1. 开发、安装,部署…...

    2024/4/20 4:24:02
  20. 每天一道算法题之K个一组翻转链表

    K个一组翻转链表 题目描述: 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。1例如: 给你这个链表:1->2->3->4->5 当 k = 2 时,…...

    2024/4/15 5:37:48

最新文章

  1. pyqt 分组框控件QGroupBox

    pyqt 分组框控件QGroupBox 分组框控件QGroupBox介绍效果代码 分组框控件QGroupBox介绍 QGroupBox提供了一个框架&#xff0c;用于将其他控件&#xff08;如按钮、滑块、标签等&#xff09;组合在一起。 QGroupBox 通常包含一个标题栏和一个内容区域。标题栏显示文本标签&#…...

    2024/5/9 21:01:52
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/7 10:36:02
  3. Go语言map、slice、channel底层实现(go面试)

    slice 切片是一个引用类型&#xff0c;其底层实现是一个结构体&#xff0c;包含以下字段&#xff1a; ptr&#xff1a;一个指向底层数组的指针&#xff0c;指针指向数组的第一个元素。 len&#xff1a;切片当前包含的元素数量。 cap&#xff1a;切片的容量&#xff0c;即底层…...

    2024/5/5 1:45:06
  4. 基于单片机的数字万用表设计

    **单片机设计介绍&#xff0c;基于单片机的数字万用表设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的数字万用表设计概要是关于使用单片机技术来实现数字万用表功能的一种设计方案。下面将详细概述该设计的各个…...

    2024/5/8 7:24:20
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/8 6:01:22
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/9 15:10:32
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/9 4:20:59
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/8 20:48:49
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/8 19:33:07
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/8 20:38:49
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/9 7:32:17
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/9 17:11:10
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57