一、设计阶段
系统分析
系统设计
功能设计
数据库设计
数据库概念模型设计
后台的功能结构分析,梳理实体、属性、和关系。并对结构进行必要的拆分。
数据库表逻辑结构设计
1、库名与应用名称尽量一致
2、表名、字段名必须使用小写字母或数字,禁止出现数字开头,
3、表名不使用复数名词
4、表的命名最好是加上“业务名称_表的作用”。如,edu_teacher
5、表必备三字段:id, gmt_create, gmt_modified
说明:
其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。
(如果使用分库分表集群部署,则id类型为verchar,非自增,业务中使用分布式id生成器)
gmt_create, gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被
动更新。
6、单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 说明:如果预计三年后的数
据量根本达不到这个级别,请不要在创建表时就分库分表。 7、表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint (1 表示是,0 表
示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀。数据库表示是与否的值,使用 tinyint 类
型,坚持 is_xxx 的 命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
8、小数类型为 decimal,禁止使用 float 和 double。 说明:float 和 double 在存储的时候,存在精度损
失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议
将数据拆成整数和小数分开存储。
9、如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
10、varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定
义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。
11、唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:uk_ 即 unique key;idx_ 即 index 的简称
12、不得使用外键与级联,一切外键概念必须在应用层解决。外键与级联更新适用于单机低并发,不适
合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速
度。
二、准备阶段
配置文件部分
- 准备项目常用的依赖配置在maven的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>com.ymz</groupId>
<artifactId>xxxx</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<!--整个项目统一字符集编码-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>12</maven.compiler.source>
<maven.compiler.target>12</maven.compiler.target>
<!--设置参数命令行为UTF-8-->
<argLine>-Dfile.encoding=UTF-8</argLine>
</properties>
<dependencies>
<!-- spring start-->
<!--aspectjweaver 支持AOP的切入点表达式 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- spring start-->
<!--cglib做动态代理-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<!--cglib做动态代理-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--Servlet 容器中相关依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- JSTL 标签库 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- JSTL 标签库 -->
<!--Servlet 容器中相关依赖-->
<!-- log start -->
<!-- log end -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- MyBatis 与 Spring 整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- MyBatis 与 Spring 整合 -->
<!--数据库连接池-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!--数据库连接池-->
<!-- Spring 进行 JSON 数据转换依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.3</version>
</dependency>
<!-- Spring 进行 JSON 数据转换依赖 -->
<!--gson Java对象和JSON数据转换-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!--gson Java对象和JSON数据转换-->
<!--分页工具-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
<!--分页工具-->
<!--spring做认证授权-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<!--spring做认证授权-->
<!--mysql数据库的包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!--mysql数据库的包-->
<!--注释配置-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
<!--注释配置-->
<!--Lombok 生成bean的get、set-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!--Lombok 生成bean的get、set-->
<!--thymeleaf模板引擎-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
<!--thymeleaf模板引擎-->
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!--文件上传-->
<!--邮箱的依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.19.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<!--邮箱的依赖-->
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<!-- target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行(对于低版本目标jdk,源代码中不能使用低版本jdk中不支持的语法),会存在target不同于source的情况 -->
<configuration>
<!-- 源代码使用的JDK版本 -->
<source>1.8</source>
<!-- 需要生成的目标class文件的编译版本 -->
<target>1.8</target>
<!-- 字符集编码,防止中文乱码 -->
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.2</version>
<configuration>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
- 配置数据库的配置文件jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db?serverTimezone=GMT%2B8
jdbc.user=root
jdbc.password=123456
# 注意不要在properties文件中采用行尾注释,容易出错
- 配置Spring配置文件spring-context.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
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-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--1. 引入jdbc.properties文件-->
<!--context:property-placeholder有一个坑
<context:property-placeholder>标签在Spring配置文件中只能存在一份,Spring容器是采用反射扫描的发现机制,
通过标签的命名空间实例化实例,当Spring探测到容器中有一个
但当有多个properties文件需要引入时采用如下方式
<context:property-placeholder location="classpath:db.properties,classpath:mail.properties"/>
-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--2. 配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--2. 配置SqlSessionFactoryBean-->
<bean id="sqlSessionFactoryBean"class="org.mybatis.spring.SqlSessionFactoryBean">
<!--2.1 装配数据源-->
<property name="dataSource" ref="dataSource"/>
<!--2.2 指定mybatis全局配置文件位置-->
<!--注意!!! configLocation 和 configuration 不能同时设置-->
<!--<property name="configLocation" value="classpath:mybatis/mybatisconfig.xml"/>-->
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="mapUnderscoreToCamelCase" value="true"/>
</bean>
</property>
<!--2.3 指定mapper配置文件位置-->
<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/>
<!--配置mybatis 插件-->
<property name="plugins">
<set>
<!--配置pageHelper 分页插件-->
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<props>
<!--方言:-->
<prop key="helperDialect">mysql</prop>
</props>
</property>
</bean>
</set>
</property>
</bean>
<!--3 配置MapperScannerConfigurer 把mapper接口类型的代理对象扫描到IOC容器中-->
<bean id="mapperScannerConfigurer"
class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--使用basePackage指定mapper所在的包-->
<property name="basePackage" value="com.ymz.ssm.mapper"/>
</bean>
<!--4 开启注解扫描-->
<context:component-scan base-package="com.ymz.ssm.service"/>
<!-- 配置注释扫描-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ymz.mapper"/>
</bean>
<!-- 配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<!-- 配置事务管理器的注释-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置事务切面 -->
<aop:config>
<!-- 考虑到后面我们整合SpringSecurity,避免把UserDetailsService加入事务控制,让切入点表达式定位到ServiceImpl -->
<aop:pointcut expression="execution(* *..*ServiceImpl.*(..))" id="txPointcut"/>
<!-- 将切入点表达式和事务通知关联起来 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务属性 -->
<tx:attributes>
<!-- 查询方法:配置只读属性,让数据库知道这是一个查询操作,能够进行一定优化 -->
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="count*" read-only="true"/>
<!-- 增删改方法:配置事务传播行为、回滚异常 -->
<!--
propagation属性:
REQUIRED:默认值,表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则自己开新事务。如果已经有了,那么就使用这个已有的事务。
顾虑:用别人的事务有可能“被”回滚。
REQUIRES_NEW:建议使用的值,表示不管当前线程上有没有事务,都要自己开事务,在自己的事务中运行。
好处:不会受到其他事务回滚的影响。
-->
<!--
rollback-for属性:配置事务方法针对什么样的异常回滚
默认:运行时异常回滚
建议:编译时异常和运行时异常都回滚
-->
<tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
<tx:method name="remove*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
<tx:method name="batch*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
</beans>
- 配置SpringMVC的配置文件
<?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:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 配置自动扫描的包:扫描handler -->
<!--扫描controller注解, 并把其他的忽略掉 -->
<context:component-scan base-package="com.ymz.*" use-default-filters="false" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--
将在 SpringMVC 上下文中定义一个DefaultServletHttpRequestHandler,它会对进入DispatcherServlet 的请求进行筛查,如果发现是没有 经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理
一般 WEB 应用服务器默认的 Servlet 的名称都是 default。若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
-->
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean id="ViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--配置视图解析器-->
<!--配置静态资源放行-->
<mvc:resources location="/plugins/" mapping="/plugins/**" />
<mvc:resources location="/layer/" mapping="/layer/**" />
<mvc:resources location="/img/" mapping="/img/**" />
<mvc:resources location="/css/" mapping="/css/**" />
<!-- 配置文件上传,如果没有使用文件上传可以不用配置,当然如果不配,那么配置文件中也不必引入上传组件包 -->
<!-- 需要apache common.fileupload jar包 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 默认编码 -->
<property name="defaultEncoding" value="utf-8" />
<!-- 文件大小最大值 -->
<property name="maxUploadSize" value="10485760000" />
<!-- 内存中的最大值 -->
<property name="maxInMemorySize" value="40960" />
</bean>
<mvc:view-controller path="/main" view-name="main"/>
</beans>
- 配置mybatis的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
- 配置mybatis-mapper的配置文件
<?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="">
</mapper>
- 配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<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/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--1. 配置ContextLoaderListener加载spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--2. 配置前端控制器-->
<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-context.xml</param-value>
</init-param>
<!--启动时初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!-- DispatcherServlet 映射的 URL 地址 -->
<!-- 大白话:什么样的访问地址会交给 SpringMVC 来处理 -->
<!-- 配置方式一:符合 RESTFUL 风格使用“/” -->
<!-- <url-pattern>/</url-pattern> -->
<!-- 配置方式二:请求扩展名 -->
<!-- <url-pattern>*.html</url-pattern>-->
<!-- <url-pattern>*.json</url-pattern>-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--3. 配置filter-->
<!--3.1 配置characterEncodingFilter 解决post请求乱码问题-->
<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>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<!--强制响应进行编码-->
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 测试数据库连接是否存在问题
// 指定Spring给junit提供的运行器类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-context.xml"})
public class TestFile {
@Autowired
private DataSource dataSource;
@Test
public void testDataSource() throws SQLException {
// 1. 通过数据源对象获取数据源连接
Connection connection = dataSource.getConnection();
// 2. 打印数据库连接
System.out.println(connection);
}
}
- 配置日志文件
使用log4j的配置文件log4j.properties
引入依赖
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.6</version>
</dependency>
<!--log4j-->
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
# log4j.appender.LOGFILE=org.apache.log4j.FileAppender
# log4j.appender.LOGFILE.File=d:\axis.log
# log4j.appender.LOGFILE.Append=true
# log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
# log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
使用logback的配置文件logback.xml
引入依赖
<!--logback-->
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 其他日志框架的中间转换包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<!--logback-->
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="INFO">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.ymz.crowd.mapper" level="DEBUG"/>
</configuration>
三、业务代码
工具类部分
- ResultEntity类( 作为后端对数据的封装,统一整个项目中Ajax请求返回的结果 )
第一种书写方式
package com.ymz.crowd.util;
/**
* 统一整个项目中Ajax请求返回的结果(未来也可以用于分布式架构各个模块间调用时返回统一类型)
* @author Lenovo
*
* @param <T>
*/
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS";
public static final String FAILED = "FAILED";
// 用来封装当前请求处理的结果是成功还是失败
private String result;
// 请求处理失败时返回的错误消息
private String message;
// 要返回的数据
private T data;
/**
* 请求处理成功且不需要返回数据时使用的工具方法
* @return
*/
public static <Type> ResultEntity<Type> successWithoutData() {
return new ResultEntity<Type>(SUCCESS, null, null);
}
/**
* 请求处理成功且需要返回数据时使用的工具方法
* @param data 要返回的数据
* @return
*/
public static <Type> ResultEntity<Type> successWithData(Type data) {
return new ResultEntity<Type>(SUCCESS, null, data);
}
/**
* 请求处理失败后使用的工具方法
* @param message 失败的错误消息
* @return
*/
public static <Type> ResultEntity<Type> failed(String message) {
return new ResultEntity<Type>(FAILED, message, null);
}
public ResultEntity() {
}
public ResultEntity(String result, String message, T data) {
super();
this.result = result;
this.message = message;
this.data = data;
}
@Override
public String toString() {
return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
第二种书写方式
package com.ymz.commonutils;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class Result {
//是否成功
private Boolean success;
//返回码
private Integer code;
//返回消息
private String message;
//返回数据
private Map<String, Object> data = new HashMap<String, Object>();
private Result(){}
public static Result ok(){
Result r = new Result();
r.setSuccess(true);
r.setCode(ResultCode.SUCCESS);
r.setMessage("成功");
return r;
}
public static Result error(){
Result r = new Result();
r.setSuccess(false);
r.setCode(ResultCode.ERROR);
r.setMessage("失败");
return r;
}
public Result success(Boolean success){
this.setSuccess(success);
return this;
}
public Result message(String message){
this.setMessage(message);
return this;
}
public Result code(Integer code){
this.setCode(code);
return this;
}
public Result data(String key, Object value){
this.data.put(key, value);
return this;
}
public Result data(Map<String, Object> map){
this.setData(map);
return this;
}
}
代码逆向生成
- 使用MybatisPlus,亲测很好用
引入依赖
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
package com.ymz.eduservice;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
public class getCode {
@Test
public void main1() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
System.out.println(projectPath);
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("ymz");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
/*
* mp生成service层代码,默认接口名称第一个字母有 I
* UcenterService
* */
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://192.168.50.128:3306/grain?serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("edu"); //模块名
pc.setParent("com.ymz");
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("edu_course_description");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
- 使用Idea插件
Free Mybatis plugin
- 生成mapper xml文件
- 快速从代码跳转到mapper及从mapper返回代码
- mybatis自动补全及语法错误提示
- 集成mybatis generator gui界面
- 根据数据库注解,生成swagger model注解
EasyCode-MybatisCodeHelper
- 基于IntelliJ IDEA开发的代码生成插件,支持自定义任意模板(Java,html,js,xml)。
- 只要是与数据库相关的代码都可以通过自定义模板来生成。支持数据库类型与java类型映射关系配置。
- 支持同时生成生成多张表的代码。每张表有独立的配置信息。完全的个性化定义,规则由你设置。
- 该版本用于兼容MybatisCodeHelper插件,方便MybatisCodeHelper插件做代码补全检测等
在IDEA中安装完之后,连接数据库
EasyCode-MybatisCodeHelper的使用
Free Mybatis plugin的使用
业务代码常见问题
前端部分
- 页面找不到静态资源404,在springMVC.xml文件中放行静态资源
<!--配置静态资源放行-->
<mvc:resources location="/plugins/" mapping="/plugins/**" />
<mvc:resources location="/layer/" mapping="/layer/**" />
<mvc:resources location="/img/" mapping="/img/**" />
<mvc:resources location="/css/" mapping="/css/**" />
- 前端采用jsp页面,静态资源的路径以及乱码的问题
<!--解决静态资源路径的问题-->
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/"/>
<!--乱码的问题-->
<!--配置filter,并且在jsp页面中设置-->
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<!DOCTYPE lang="zh-CN">
- 解决复选框取值问题
// 复选框取值
$("input:checkbox:checked").val()
或者
$("input:[type='checkbox']:checked").val();
或者
$("input:[name='ck']:checked").val();
- 获取多个checkbox选中项
$('input:checkbox').each(function() {
if ($(this).prop('checked') ==true) {
alert($(this).val());
}
});
- 单选框的选中问题
<input type="radio" value="aaaa">aaa
<input type="radio" value="bbbb">bbb
<script>
$(function{
// 去除单选框的value值
var a = $("input:radio:checked").val()
// 选中的单选框为true
var a = $("input:radio").prop('checked')
$('input:radio').click(function(){
var $radio = $(this);
if ($radio.data('checked')){
$radio.prop('checked', false);
$radio.data('checked', false);
} else {
$radio.prop('checked', true);
$radio.data('checked', true);
}
});
})
</script>
- js的时间Date() 转换指定格式
Date.prototype.format = function (fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
}
var commentTime = new Date().format("yyyy-MM-dd hh:mm:ss");
- 弹出和收起盒子
var flag = 1;
if (0 == flag) {
$("#"+boxId+"box").slideUp("10000");
flag = 1;
} else {
$("#"+boxId+"box").removeAttr('hidden')
$("#"+boxId+"box").slideDown("10000");
flag = 0;
}
后端部分
Mybatis
- Mybatis查询多个bean
<resultMap id="bean4News" type="com.ymz.entity.News">
<result column="id" property="id"/>
<result column="content" property="content"/>
<result column="public_time" property="publicTime"/>
<result column="share" property="share"/>
<result column="favorite" property="favorite"/>
<result column="resident_id" property="residentId"/>
</resultMap>
<select id="findNews" parameterType="map" resultMap="bean4News">
select id, content, public_time, share, favorite, resident_id from news
</select>
<!--其中column代表的是数据库中的表列-->
- Mybatis插入数据时返回主键
<insert id="insertIntoComment" parameterType="com.ymz.entity.PO.CommentsPO"
useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into comments(comment,comment_time)
values (
#{comment,jdbcType=INTEGER},
#{commentTime,jdbcType=TIMESTAMP}
)
</insert>
- mybatis插入数据时设置默认属性
#{workType,jdbcType=VARCHAR},
#{content,jdbcType=VARCHAR},
#{income,jdbcType=DOUBLE},
#{residentId,jdbcType=INTEGER},
#{occurredTime,jdbcType=TIMESTAMP}
<!--TIMESTAMP会将date类型转换 yyyy-MM-dd hh-mm-ss-->
邮箱
- 引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.19.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
- 配置mail.properties, 注意不要用行尾注释
#服务器主机名 smtp.xx.com
mail.host=smtp.qq.com
mail.username=111111111@qq.com
#密码/客户端授权码
mail.password=你的授权码
#编码字符
mail.defaultEncoding=utf-8
#是否进行用户名密码校验
mail.auth=true
#设置超时时间
mail.timeout=20000
# 注意不要在properties文件中采用行尾注释,容易出错
- 配置bean对象,在spring的配置文件
<!--
<context:property-placeholder>标签在Spring配置文件中只能存在一份!!!Spring容器是采用反射扫描的发现机制,
通过标签的命名空间实例化实例,当Spring探测到容器中有一个
-->
<context:property-placeholder location="classpath:db.properties,classpath:mail.properties"/>
<!-- 配置邮箱-->
<!-- 切不可在properties后面用行尾注释,教训-->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mail.host}"/>
<property name="username" value="${mail.username}"/>
<property name="password" value="${mail.password}"/>
<property name="defaultEncoding" value="${mail.defaultEncoding}"/>
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.auth">${mail.auth}</prop>
<prop key="mail.smtp.timeout">${mail.timeout}</prop>
</props>
</property>
</bean>
<bean id="simpleMailMessage" class="org.springframework.mail.SimpleMailMessage">
<!-- 发件人email -->
<property name="from" value="${mail.username}" />
</bean>
<bean id="mailUtil" class="com.ymz.utils.MailUtil">
<property name="mailSender" ref="mailSender"></property>
<property name="simpleMailMessage" ref="simpleMailMessage"></property>
</bean>
- 配置邮件的工具类,可选,最后使用的时候
package com.ymz.utils;
import java.util.List;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.web.multipart.MultipartFile;
public class MailUtil {
private JavaMailSenderImpl mailSender;
/**
* JavaMailSenderImpl支持MimeMessages和SimpleMailMessages。
* MimeMessages为复杂邮件模板,支持文本、附件、html、图片等。
* SimpleMailMessages实现了MimeMessageHelper,为普通邮件模板,支持文本
*/
private SimpleMailMessage simpleMailMessage;
/**
* 描述:Spring 依赖注入
*/
public void setMailSender(JavaMailSenderImpl mailSender) {
this.mailSender = mailSender;
}
/**
* 描述:Spring 依赖注入
*/
public void setSimpleMailMessage(SimpleMailMessage simpleMailMessage) {
this.simpleMailMessage = simpleMailMessage;
}
/**
* 单发
*
* @param recipient 收件人
* @param subject 主题
* @param content 内容
*/
public void send(String recipient,String subject,String content){
System.out.println(simpleMailMessage);
simpleMailMessage.setTo(recipient);
simpleMailMessage.setSubject(subject);
simpleMailMessage.setText(content);
mailSender.send(simpleMailMessage);
}
/**
* 群发
*
* @param recipients 收件人
* @param subject 主题
* @param content 内容
*/
public void send(List<String> recipients,String subject,String content){
simpleMailMessage.setTo(recipients.toArray(new String[recipients.size()]));
simpleMailMessage.setSubject(subject);
simpleMailMessage.setText(content);
mailSender.send(simpleMailMessage);
}
/**
* 发送带附件的邮件
* @author malizhi
* @param recipient
* @param subject
* @param content
* @param file void
* @version 1.0
* @exception
*/
public void sendWithFile(String recipient,String subject,String content,MultipartFile file){
//使用JavaMail的MimeMessage,支付更加复杂的邮件格式和内容
MimeMessage msg = mailSender.createMimeMessage();
try {
//创建MimeMessageHelper对象,处理MimeMessage的辅助类
MimeMessageHelper helper = new MimeMessageHelper(msg, true);
//使用辅助类MimeMessage设定参数
helper.setFrom(mailSender.getUsername());
helper.setTo(recipient);
helper.setSubject(subject);
helper.setText(content);
//加入附件
helper.addAttachment(file.getOriginalFilename(), file);
} catch (MessagingException e) {
e.printStackTrace();
}
// 发送邮件
mailSender.send(msg);
}
}