项目层级划分
1.Dao层:持久层,主要与数据库交互
DAO层首先会创建Dao接口,接着就可以在配置文件中定义该接口的实现类;接着就可以在模块中调用Dao的接口进行数据业务的处理,而不用关注此接口的具体实现类是哪一个类,Dao层的数据源和数据库连接的参数都是在配置文件中进行配置的。
2.Entity层:实体层,数据库在项目中的类
主要用于定义与数据库对象应的属性,提供get/set方法,tostring方法,有参无参构造函数。
3.Service层:业务层 控制业务
业务模块的逻辑应用设计,和DAO层一样都是先设计接口,再创建要实现的类,然后在配置文件中进行配置其实现的关联。接下来就可以在service层调用接口进行业务逻辑应用的处理。
好处:封装Service层的业务逻辑有利于业务逻辑的独立性和重复利用性。
4.Controller层:控制层 控制业务逻辑
具体的业务模块流程的控制,controller层主要调用Service层里面的接口控制具体的业务流程,控制的配置也要在配置文件中进行。
Controller和Service的区别是:Controller负责具体的业务模块流程的控制;Service层负责业务模块的逻辑应用设计
总结:具体的一个项目中有:controller层调用了Service层的方法,Service层调用Dao层的方法,其中调用的参数是使用Entity层进行传递的。
补充:5、View层
此层与控制层结合比较紧密,需要二者结合起来协同工发。View层主要负责前台jsp页面的表示,
小结
DAO创建接口,在Mapper中进行实现,Entity层是具体的单个实体.
数据库设计
创建数据库, 表
SHOW DATABASES; -- 显示数据库
USE `group1`; --指定数据库
create database if not exists `group1`; -- 创建数据库
DROP DATABASE [IF EXISTS] database_name; -- 删除数据库
drop table if exists `table_name`; -- 删除数据表
create table `table_name`; -- 创建数据表
ENGINE = INNODB; -- 数据库引擎
AUTO_INCREMENT = 19; -- 自增长的起始值
CHARACTER SET = utf8; --设置字符集
COLLATE = utf8_bin; -- 字符集校验, 区分大小写
ROW_FORMAT = COMPACT;
PRIMARY KEY (`id`) ; --指定主键
USING BTREE; -- hash 索引和B树索引
COMMENT '备注' -- 给表的一列添加注释
NULL DEFAULT 0 / NULL --可以没有值,默认为0 或者NULL
数据类型:
- 数值型: int
- 浮点型: 定点数dec(M,D) 浮点数float(M,D)
- 字符型 可变长字符varchar(M), char(M)
- 日期型 datetime / timestampe
MySQL:
<select id="selectAll" resultMap="agents">
select mid, mname, mlevelid, mtel, mino, mbno, mba, mto, mre, mse, mend, mreg, mref, levelname, profit, mflag from
(SELECT m.id mid, m.`name` mname, m.levelid mlevelid, m.tel mtel, m.id_no mino, m.bank_no mbno,
m.balance mba, m.total mto, m.real_status mre, m.ser_status mse, m.endtime mend, m.regtime mreg,
m.reference mref, m.flag mflag
FROM (SELECT * FROM member WHERE reference IS NOT NULL) m,
(SELECT @pid := #{id}) pd
WHERE FIND_IN_SET(reference, @pid) > 0
AND @pid := concat(@pid, ',', id)) mem inner join `level` on mem.mlevelid = `level`.id where mflag = 0
</select>
<!-- 注释下上面的递归查询,首先执行的是
1. (SELECT * FROM member WHERE reference IS NOT NULL) m 查询所有的代理不为0的成员信息
2. 从外部接受一个@pid的值
3. 如果查询出来的代理reference 在@pid中,那么就说明该成员的上级为@pid中的值,即为下级代理,则加入@pid列表中
4. 此时@pid的条件改变,所以需要继续执行where的判断语句
5, 返回自身以及自身的下级代理,此时由于@pid改变 @pid := concat(@pid, ',', id),所以继续判断,where是否有上级代理在@pid中
6,如果有的话,加入其id到@pid中,然后输出本级代理
-->
-
自定义函数功能
find_in_set 函数
函数语法:find_in_set(str,strlist)
str 代表要查询的字符串 , strlist 是一个以逗号分隔的字符串,如 (‘a,b,c’)。
此函数用于查找 str 字符串在字符串 strlist 中的位置,返回结果为 1 ~ n 。若没有找到,则返回0。
#向下递归
delimiter $$
drop function if exists get_child_list$$
create function get_child_list(in_id varchar(10)) returns varchar(1000)
begin
declare ids varchar(1000) default '';
declare tempids varchar(1000);
set tempids = in_id;
while tempids is not null do
set ids = CONCAT_WS(',',ids,tempids);
select GROUP_CONCAT(id) into tempids from dept where FIND_IN_SET(pid,tempids)>0;
end while;
return ids;
end
$$
delimiter ;
#使用方式:
select * from dept where FIND_IN_SET(id, get_child_list('1010'));
#向上递归:向上递归时,每一层递归一个子节点只对应一个父节点
delimiter $$
drop function if exists get_parent_list$$
create function get_parent_list(in_id varchar(10)) returns varchar(1000)
begin
declare ids varchar(1000);
declare tempid varchar(10);
set tempid = in_id;
while tempid is not null do
set ids = CONCAT_WS(',',ids,tempid);
select pid into tempid from dept where id=tempid;
end while;
return ids;
end
$$
delimiter ;
#使用方式:
select * from dept where FIND_IN_SET(id, get_parent_list('1010'));
解释:
(1) delimiter $$ ,用于定义结束符。我们知道 MySQL 默认的结束符为分号,表明指令结束并执行。
但是在函数体中,有时我们希望遇到分号不结束,因此需要暂时把结束符改为一个随意的其他值。
我这里设置为 $$,意思是遇到 $$ 才结束,并执行当前语句。
(2)drop function if exists get_child_list$$ 。若函数 get_child_list 已经存在了,
则先删除它。注意这里需要用 当前自定义的结束符 $$ 来结束并执行语句。因为,
这里需要数和下边的函体单独区分开来执行
(3)create function get_child_list 创建函数。并且参数传入一个根节点的子节点id,
需要注意一定要注明参数的类型和长度,如这里是 varchar(10)。returns varchar(1000)
用来定义返回值参数类型。
(4)begin 和 end 中间包围的就是函数体。用来写具体的逻辑。
(5)declare 用来声明变量,并且可以用 default 设置默认值。这里定义的 ids 即作为
整个函数的返回值,是用来拼接成最终我们需要的以逗号分隔的递归串的。而 tempids
是为了记录下边 while 循环中临时生成的所有子节点以逗号拼接成的字符串。
(6) set 用来给变量赋值。此处把传进来的根节点赋值给 tempids 。
(7) while do ... end while; 循环语句,循环逻辑包含在内。注意,end while 末尾需要加上分号。
循环体内,先用 CONCAT_WS 函数把最终结果 ids 和 临时生成的 tempids 用逗号拼接起来。
然后以 FIND_IN_SET(pid,tempids)>0 为条件,遍历在 tempids 中的所有 pid ,
寻找以此为父节点的所有子节点 id ,并且通过 GROUP_CONCAT(id) into tempids
把这些子节点 id 都用逗号拼接起来,并覆盖更新 tempids 。
等下次循环进来时,就会再次拼接 ids ,并再次查找所有子节点的所有子节点。
循环往复,一层一层的向下递归遍历子节点。直到判断 tempids 为空,说明所有
子节点都已经遍历完了,就结束整个循环。
(8)return ids; 用于把 ids 作为函数返回值返回。
(9)函数体结束以后,记得用结束符 $$ 来结束整个逻辑,并执行。
(10)最后别忘了,把结束符重新设置为默认的结束符分号 。
-
resultType
resultType可以把查询结果封装到pojo类型中,但必须pojo类的属性名和查询到的数据库表的字段名一致。如果sql查询到的字段与pojo的属性名不一致,则需要使用resultMap将字段名和属性名对应起来,进行手动配置封装,将结果映射到pojo中 -
resultMap:
上面的递归查询的结果输出是一个resultMap,
id: 对应上面resultMap的指定agents
type:对应实体类Agent
id 和 result 都映射一个单独列的值到简单数据类型(字符 串,整型,双精度浮点数,日期等)的单独属性或字段。
名称 | 作用 |
---|---|
property | 映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同 的 JavaBeans 的属性,那么就会使用。否则 MyBatis 将会寻找给定名称 property 的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你 可以这样映射一些东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。 |
column | 从数据库中得到的列名,或者是列名的重命名标签。这也是通常和会 传递给 resultSet.getString(columnName)方法参数中相同的字符串。 |
<!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
<!-- id:设置ResultMap的id -->
<resultMap type="order" id="orderResultMap">
<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
<!-- property:主键在pojo中的属性名 -->
<!-- column:主键在数据库中的列名 -->
<id property="id" column="id" />
<!-- 定义普通属性 -->
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
</resultMap>
MySQL使用技巧:
使用trim, prefix, suffix 等 更新多个参数
<trim prefix="" suffix="" suffixOverrides="" prefixOverrides=""></trim>
prefix:在trim标签内sql语句加上前缀。
suffix:在trim标签内sql语句加上后缀。
suffixOverrides:指定去除多余的后缀内容,如:suffixOverrides=",",去除trim标签内sql语句多余的后缀","。假设没有指定
suffixOverrides="," 执行的sql语句也许是这样的:insert into cart (id,user_id,deal_id,) values(1,2,1,);
显然是错误的指定之后语句就会变成insert into cart (id,user_id,deal_id) values(1,2,1);这样就将“,”去掉了。
prefixOverrides:指定去除多余的前缀内容
<update id="updateLevelApplicationform" parameterType="com.woniuxy.entity.Levelapplicationform">
UPDATE levelApplicationForm
<trim prefix="set" suffixOverrides=",">
<if test="nowlevel!=null">nowlevel=#{nowlevel},</if>
<if test="levelapply!=null">levelapply=#{levelapply},</if>
<if test="describe!=null">`describe`=#{describe},</if>
<if test="status!=null">status=#{status},</if>
<if test="memberid!=null">memberid=#{memberid},</if>
<if test="referenceid!=null">referenceid=#{referenceid},</if>
<if test="newreferenceid!=null">newreferenceid=#{newreferenceid},</if>
<if test="applicationtime!=null">applicationtime=#{applicationtime},</if>
<if test="disposetime!=null">disposetime=#{disposetime},</if>
</trim>
WHERE id=#{id}
</update>
- 按照名称查询:
使用concat(concat('%', #{name}), '%')
名称查询,只要包含就可以查询到
<!-- 按照名称选择银行 -->
<select id="selectBankLike" resultType="com.woniuxy.entity.Bank">
select *
from bank where `name` like concat(concat('%', #{name}), '%')
</select>
- 使用foreach删除列表所选
关键字: foreach; collection集合; open开始; close结束; item元素
<delete id="deleteSelect">
delete from banner where id in
<foreach collection="array" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
- 对有自增主键的表添加数据
当主键是自增的情况下,添加一条记录的同时,其主键是不能使用的,但是有时我们需要该主键,使用下列语句
useGeneratedKeys
:默认为false,为true时,表示如果插入的表以自增列为主键,则允许 JDBC 支持自动生成主键,并可将自动生成的主键返回;
keyProperty
:实体类对应主键的属性;
keyColumn
:数据库中的主键;
<insert id="insert" keyProperty="id" useGeneratedKeys="true">
insert into group1.level(levelname, profit)
values (#{levelname}, #{profit})
</insert>
-
POJO:
一个简单的Java类,这个类没有实现/继承任何特殊的java接口或者类,不遵循任何主要java模型,约定或者框架的java对象。在理想情况下,POJO不应该有注解。 -
JavaBean:
JavaBean是可序列化的,实现了serializable接口
具有一个无参构造器
有按照命名规范的set和gett,is(可以用于访问布尔类型的属性)方法 -
MySQL中#{}和${}的区别
1 # 与 $ 的区别
mybatis中使用ParameterType向sql语句传参,在sql语句中引用这些参数的时候,有两种方式:#parameterName, $parameterName。两者的区别:使用#parameterName方式引用参数的时候,Mybatis会把传入的参数当成是一个字符串,自动添加双引号。$parameterName引用参数时,不做任何处理,直接将值拼接在sql语句中。
#是一个占位符,$是拼接符。
2 如何防止sql注入
使用 # 能够防止sql注入,$不能避免注入攻击。#的方式引用参数,mybatis会先对sql语句进行预编译,然后再引用值,能够有效防止sql注入,提高安全性。$的方式引用参数,sql语句不进行预编译。
#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”。
$将传入的数据直接显示生成在sql中。如:order by userid,如果传入的值是111,那么解析成sql时的值为order by 111, 如果传入的值是id,则解析成的sql为order by id。不会有双引号,可能导致sql注入问题。
#方式能够很大程度防止sql注入;$方式无法防止Sql注入
ORM对象/关系数据库映射
当使用一种面向对象的编程语言来进行应用开发时,从项目一开始就采用的是面向对象分析,面向对象设计,面向对象编程,但到了持久层数据库访问时,又必须重返关系数据库的访问方式,这是一种非常糟糕的感觉.于是人们需要一种工具,它可以把关系型数据库包装成面向对象的模型.它就是ORM.
ORM框架是面向对象程序设计语言与关系数据库发展不同步时的中间解决方案.随着面向对象数据库的发展,其理论逐步完善,最终面向对象数据库会取代关系型数据库.只是这个过程不可一蹴而就,ORM框架在此期间会蓬勃发展.但随着面向对象数据库的广泛使用,ORM工具会逐渐消亡.
Spring
属性
:
@Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。 @Resource(name = “name”, type=Type.class)
@Resource装配顺序
1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
@Service用于标注业务层组件
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件,即DAO组件
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
-
- 什么是Bean:
bean就相当于定义一个组件,这个组件是用于具体实现某个功能的。这里的所定义的bean就相当于给了你一个简洁方便的方法来调用这个组件实现你要完成的功能。
1、Java面向对象,对象有方法和属性,那么就需要对象实例来调用方法和属性(即实例化);
2、凡是有方法或属性的类都需要实例化,这样才能具象化去使用这些方法和属性;
3、规律:凡是子类及带有方法或属性的类都要加上注册Bean到Spring IoC的注解;(@Component , @Repository , @ Controller , @Service , @Configration)
4、把Bean理解为类的代理或代言人(实际上确实是通过反射、代理来实现的),这样它就能代表类拥有该拥有的东西了
5、我们都在微博上@过某某,对方会优先看到这条信息,并给你反馈,那么在Spring中,你标识一个@符号,那么Spring就会来看看,并且从这里拿到一个Bean(注册)或者给出一个Bean(使用)
- 什么是Bean:
-
一、注解(annotations)列表
@SpringBootApplication: 包含了@ComponentScan、@Configuration和@EnableAutoConfiguration注解。其中@ComponentScan让spring Boot扫描到Configuration类并把它加入到程序上下文。
@Configuration 等同于spring的XML配置文件;使用Java代码可以检查类型安全。
@EnableAutoConfiguration 自动配置。
@ComponentScan 组件扫描,可自动发现和装配一些Bean。
@Component 可配合CommandLineRunner使用,在程序启动后执行一些基础任务。
@RestController 注解是@Controller和@ResponseBody的合集,表示这是个控制器bean,并且是将函数的返回值直接填入HTTP响应体中,是REST风格的控制器。
@Autowired 自动导入。
@PathVariable 获取参数。
@JsonBackReference 解决嵌套外链问题。
@RepositoryRestResourcepublic 配合spring-boot-starter-data-rest使用。
@SpringBootApplication: 申明让spring boot自动给程序进行必要的配置,这个配置等同于:@Configuration ,@EnableAutoConfiguration 和 @ComponentScan 三个配置。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@ResponseBody: 表示该方法的返回结果直接写入HTTP response body中,一般在异步获取数据时使用,用于构建RESTful的api。在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。
比如异步获取json数据,加上@responsebody后,会直接返回json数据。该注解一般会配合@RequestMapping一起使用。
示例代码:
@RequestMapping(“/test”)
@ResponseBody
public String test(){
return”ok”;
}
@Controller: 用于定义控制器类,在spring 项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层),一般这个注解在类中,通常方法需要配合注解@RequestMapping。
示例代码:
@Controller
@RequestMapping(“/demoInfo”)
publicclass DemoController {
@Autowired
private DemoInfoService demoInfoService;
@RequestMapping("/hello")
public String hello(Map<String,Object> map){
System.out.println("DemoController.hello()");
map.put("hello","from TemplateController.helloHtml");
//会使用hello.html或者hello.ftl模板进行渲染显示.
return"/hello";
}
}
@RestController: 用于标注控制层组件(如struts中的action),@ResponseBody和@Controller的合集。
示例代码:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/demoInfo2”)
publicclass DemoController2 {
@RequestMapping("/test")
public String test(){
return"ok";
}
}
@RequestMapping: 提供路由信息,负责URL到Controller中的具体函数的映射。
@EnableAutoConfiguration: Spring Boot自动配置(auto-configuration):尝试根据你添加的jar依赖自动配置你的Spring应用。例如,如果你的classpath下存在HSQLDB,并且你没有手动配置任何数据库连接beans,那么我们将自动配置一个内存型(in-memory)数据库”。
你可以将@EnableAutoConfiguration或者@SpringBootApplication注解添加到一个@Configuration类上来选择自动配置。如果发现应用了你不想要的特定自动配置类,你可以使用@EnableAutoConfiguration注解的排除属性来禁用它们。
@ComponentScan: 表示将该类自动发现扫描组件。个人理解相当于,如果扫描到有@Component、@Controller、@Service等这些注解的类,并注册为Bean,可以自动收集所有的Spring组件,包括@Configuration类。
我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。可以自动收集所有的Spring组件,包括@Configuration类。我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。
如果没有配置的话,Spring Boot会扫描启动类所在包下以及子包下的使用了@Service,@Repository等注解的类。
@Configuration: 相当于传统的xml配置文件,如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。
@Import: 用来导入其他配置类。
@ImportResource: 用来加载xml配置文件。
@Autowired: 自动导入依赖的bean
@Service: 一般用于修饰service层的组件
@Repository: 使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。
@Bean: 用@Bean标注方法等价于XML中配置的bean。
@Value: 注入Spring boot application.properties配置的属性的值。
示例代码:
@Value(value = “#{message}”)
private String message;
@Inject: 等价于默认的@Autowired,只是没有required属性;
@Component: 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Bean: 相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
@AutoWired: 自动导入依赖的bean。byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false)时,就算找不到bean也不报错。
@Qualifier: 当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用。@Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式如下:
@Autowired
@Qualifier(value = “demoInfoService”)
private DemoInfoService demoInfoService;
@Resource(name=”name”,type=”type”): 没有括号内内容的话,默认byName。与@Autowired干类似的事。
@Entity: @Table(name=”“): 表明这是一个实体类。一般用于jpa这两个注解一般一块使用,但是如果表名和实体类名相同的话,@Table可以省略
@MappedSuperClass: 用在确定是父类的entity上。父类的属性子类可以继承。
@NoRepositoryBean: 一般用作父类的repository,有这个注解,spring不会去实例化该repository。
@Column: 如果字段名与列名相同,则可以省略。
@Id: 表示该属性为主键。
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = “repair_seq”): 表示主键生成策略是sequence(可以为Auto、IDENTITY、native等,Auto表示可在多个数据库间切换),指定sequence的名字是repair_seq。
@SequenceGeneretor(name = “repair_seq”, sequenceName = “seq_repair”, allocationSize = 1): name为sequence的名称,以便使用,sequenceName为数据库的sequence名称,两个名称可以一致。
@Transient: 表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性。如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic。@Basic(fetch=FetchType.LAZY):标记可以指定实体属性的加载方式
@JsonIgnore: 作用是json序列化时将Java bean中的一些属性忽略掉,序列化和反序列化都受影响。
@JoinColumn(name=”loginId”): 一对一:本表中指向另一个表的外键。一对多:另一个表指向本表的外键。
@OneToOne、@OneToMany、@ManyToOne: 对应hibernate配置文件中的一对一,一对多,多对一。
@RequestMapping: @RequestMapping(“/path”)表示该控制器处理所有“/path”的UR L请求。RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。该注解有六个属性:
params:指定request中必须包含某些参数值是,才让该方法处理。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
value:指定请求的实际地址,指定的地址可以是URI Template 模式
method:指定请求的method类型, GET、POST、PUT、DELETE等
consumes:指定处理请求的提交内容类型(Content-Type),如application/json,text/html;
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
@RequestParam: 用在方法的参数前面。
@RequestParam String a =request.getParameter(“a”)。
@PathVariable: 路径变量。如
@Param: @Param是MyBatis所提供的(org.apache.ibatis.annotations.Param),作为Dao层的注解,作用是用于传递参数,从而可以与SQL中的的字段名相对应,一般在2=<参数数<=5时使用最佳。
RequestMapping(“user/get/mac/{macAddress}”)
public String getByMacAddress(@PathVariable String macAddress){
//do something;
}
参数与大括号里的名字一样要相同。
@ControllerAdvice: 包含@Component。可以被扫描到。统一处理异常。
@ExceptionHandler(Exception.class): 用在方法上面表示遇到这个异常就执行以下方法。
前端Vue
Vue.js
在讲Vue之前,需要大概了解下 HTML、CSS、JS是什么?
HTML是写标签的;CSS是写样式的;JS是给网页增加动态效果
Vue不需要操作Dom,实现了MVVM
前端Vue有API与后端进行对接
Router进行页面的路由转发
后端
前后端数据交互
-
前端Vue的axios请求到后端的Controller层进行处理,返回规定好的数据格式(映射到ResultMap)。
-
什么是前后端分离:
- 后台代码看不到前端代码
- 后台开发接口可以用postman测试,不用管前端界面。
- 前端开发页面可以mock测试数据,不用管后台接口的开发。
- 前后端基于json传输数据,基于resultFul风格的请求响应。
- 前后端适当的时候进行接口联调。 前后端分离到底有哪些好处
- 各司其职
- 并行开发
Tomcat
tomcat含义
tomcat是一个轻量级应用服务器,是支持运行Servlet/JSP应用程序的容器,运行在jvm上,绑定IP地址并监听TCP端口。
它是由Apache推出的一款免费开源的Servlet容器,可实现JavaWeb程序的装载,是配置JSP(Java Server Page)和JAVA系统必备的一款环境。它也具有传统的Web服务器的功能:处理Html页面。但是与Apache相比,在处理静态Html上的能力略逊一筹。
Tomcat运行时占用的系统资源小,扩展性好,支持负载平衡与邮件服务等开发应用系统常用的功能,因而深受java爱好者的喜爱,并得到了部分软件开发商的认可,和Apache一样,早已成为主流Web服务器的一种。
tomcat作用
(1)管理servlet应用的生命周期
(2)把客户端请求的url映射到对应的servlet
(3)与Servlet程序合作处理HTTP请求
通俗点说他是jsp网站的服务器之一,就像asp网站要用到微软的IIS服务器,php网站用apache服务器一样,因为你的jsp动态网站使用脚本语言等写的,需要有服务器来解释你的语言吧,服务器就是这个功能。如果你的网页是纯html的,浏览器就可以直接解释查看效果,但是你的网页一但是.jsp .asp .php 等的动态网页时浏览器自己就无法解释了,需要上面说到的服务器。tomcat便可以解释jsp等java编写的网站。
序列化:
什么情况下需要序列化:
public class Transaction implements Serializable { }
原文:https://blog.csdn.net/dinghqalex/article/details/46009911
1. 当你想把的内存中的对象写入到硬盘的时候。
2. 当你想用套接字在网络上传送对象的时候。
3. 当你想通过RMI传输对象的时候。
稍微解释一下:
1. 【保存在硬盘中时】比如说你的内存不够用了,那计算机就要将内存里面的一部分对象暂时的保存到硬盘中,等到要用的时候再读入到内存中,硬盘的那部分存储空间就是所谓的虚拟内存。在比如过你要将某个特定的对象保存到文件中,我隔几天在把它拿出来用,那么这时候就要实现Serializable接口。
2. 【网络上传输时】在进行Java的Socket编程的时候,你有时候可能要传输某一类的对象,那么也就要实现Serializable接口。最常见的你传输一个字符串,它是JDK里面的类,也实现了Serializable接口,这样做为的是将数据变为二进制来传输,所以可以在网络上传输。
3. 【远程方法调用时
DAO层,数据库接口
-
Advice 建议
-
List<Advice> queryByTime(@Param("start") String start,@Param("end") String end);
-
List<Advice> queryAll();
-
int update(int id, String note, int status);
-
int addAdvice(String advice, String tel);
-
List<Advice> selectPending();
-
-
AgentLevelSettingService 代理人等级设置
-
AgentService 代理人
-
Bank 银行
-
Banner 轮播图
-
Level 等级
-
Loginmsg 登录信息
-
Member 成员
-
Role 角色
-
Transaction 交易
-
User 用户角色权限表
- rbac_role 角色表
- rbac_user 用户表
- rbac_user_role 角色、用户转换表
DAO ==》 Mapper
Service + DAO == 》 Impl
Entity == 》实体类
Controller == 》 控制类,逻辑代码,API接口,与前端有Vue交互
注解
@RequestMapping(" ")
@ResponseBody
@RestController
pagehelper
1.pom.xml中对pagehelper进行配置
<!--分页依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
2.在controller内中对页面进行分页设置
public ResponseEntity<PageInfo<Transaction>> selectByTransaction(Integer pageIndex ){
# 1.对当前页面进行PageInfo分页设置,startPage(int pageNum, int pageSize)
PageHelper.startPage(pageIndex,5);
# 2.设置返回的数据格式,为交易的列表,但是列表是由PageInfo进行分页封装
PageInfo<Transaction> transactionList=agentService.selectTransactionPriceByMemberid(pageIndex);
# 3.对返回的数据设置其返回类型,返回响应体ResponseEntity
ResponseEntity<PageInfo<Transaction>> pageInfoResponseEntity = new ResponseEntity<>(transactionList);
# 4.返回预设的响应对象
return pageInfoResponseEntity;
}
Swagger
解释
前后端分离
- 前端 -> 前端控制层、视图层
- 后端 -> 后端控制层、服务层、数据访问层
- 前后端通过API进行交互
- 前后端相对独立且松耦合
产生的问题
- 前后端集成,前端或者后端无法做到“及时协商,尽早解决”,最终导致问题集中爆发
解决方案
- 首先定义schema [ 计划的提纲 ],并实时跟踪最新的API,降低集成风险
Swagger
- 号称世界上最流行的API框架
- Restful Api 文档在线自动生成器 => API 文档 与API 定义同步更新
- 直接运行,在线测试API
- 支持多种语言 (如:Java,PHP等)
- 官网:https://swagger.io/
1.导入相关依赖包
SpringBoot集成Swagger => springfox,两个jar包,要求:jdk 1.8 + 否则swagger2无法运行
Springfox-swagger2
swagger-springmvc
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2
-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swaggerui
-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2.我们需要编写一个配置类-SwaggerConfig来配置 Swagger
- Docket 实例关联上 apiInfo(),apis()里面需要填写扫描的controller位置
- 重启项目,访问测试 http://localhost:8080/swagger-ui.html 看下效果;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()) //绑定apiInfo信息
.select() //通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
.apis(RequestHandlerSelectors.basePackage("com.woniuxy.controller")) //绑定controller信息
.paths(PathSelectors.any()) //配置path过滤
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("API测试文档")
.description("DEMO项目的接口测试文档")
.termsOfServiceUrl("http://www.hangge.com")
.version("1.0")
.contact(new Contact("航歌",
"http://www.hangge.com",
"hangge@hangge.com"))
.build();
}
}
3.实际使用
-
首先我们在 Controller 上添加相关的 @Api 注解:
(1)@Api 注解标注在类上用来描述整个 Controller 信息。
(2)@ApiOperation 注解标注在方法上,用来描述一个方法的基本信息。
(3)@ApiImplicitParam 注解标注在方法上,用来描述方法的参数。其中 paramType 是指方法参数的类型,有如下可选值:path:参数获取方式为 @PathVariable query:参数获取方式为 @RequestParam header:参数获取方式为 @RequestHeader body form
(4)如果有多个参数,可以将多个参数的 @ApiImplicitParam 注解放到 @ApiImplicitParams 中。
(5)@ApiResponse 是对响应结果的描述。code 表示响应码,message 为相应的描述信息。如果有多个 @ApiResponse,则放在一个 @ApiResponses 中。
(6)@ApiIgnore 注解表示不对某个接口生成文档。
@RestController
@Api(tags = "用户数据接口")
public class UserController {
@ApiOperation(value ="查询用户", notes = "根据 id 查询用户")
@ApiImplicitParam(paramType = "path", name = "id", value = "用户 id", required = true)
@GetMapping("/user/{id}")
public String getUserById(@PathVariable Integer id){
return "查找的用户id是:" + id;
}
@ApiOperation(value = "新增用户", notes = "根据传入的用户名和密码添加一个新用户")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", name = "username",
value = "用户名", required = true, defaultValue = "test"),
@ApiImplicitParam(paramType = "query", name = "password",
value = "密码", required = true, defaultValue = "123")
})
@PostMapping("/user")
public String addUser(@RequestParam String username, @RequestParam String password) {
return "新增用户:" + username + " " + password;
}
@ApiOperation(value = "删除用户", notes = "根据 id 删除用户")
@ApiResponses({
@ApiResponse(code = 200, message = "删除成功!"),
@ApiResponse(code = 500, message = "删除失败!")
})
@DeleteMapping("/user/{id}")
public Integer deleteUserById(@PathVariable Integer id) {
return id;
}
@ApiOperation(value = "修改用户", notes = "传入用户信息进行更新修改")
@PutMapping("/user")
public String updateUser(@RequestBody User user){
return user.toString();
}
@ApiIgnore
@GetMapping("/user/test")
public String test() {
return "这是一个测试接口,不需要在api文档中显示。";
}
}
4.测试
项目启动后,swagger-bootstrap-ui通过 http://localhost:8080/doc.html 地址即可访问新的 UI 界面。springfox-swagger-ui中输入 http://localhost:8080/swagger-ui.html 即可看到接口文档。
5.注意Vue解决跨域问题链接
-
Vue前端解决
既然使用axios直接进行跨域访问不可行,我们就需要配置代理了。代理可以解决的原因:因为客户端请求服务端的数据是存在跨域问题的,而服务器和服务器之间可以相互请求数据,是没有跨域的概念(如果服务器没有设置禁止跨域的权限问题),也就是说,我们可以配置一个代理的服务器可以请求另一个服务器中的数据,然后把请求出来的数据返回到我们的代理服务器中,代理服务器再返回数据给我们的客户端,这样我们就可以实现跨域访问数据。
-
后端配置CorsFliter解决
axios
axios({
url:'',
method:"post",
data:{
}
......
})
使用的是get请求,所以传给后端时用的是params。如果使用的是post请求,那么传给后端时用的就是data。
@RequestMapping 和 @GetMapping @PostMapping 区别
@GetMapping
用于将HTTP GET请求映射到特定处理程序方法的注释。具体来说,@GetMapping是一个作为快捷方式的组合注释
@RequestMapping(method = RequestMethod.GET)。
@PostMapping
用于将HTTP POST请求映射到特定处理程序方法的注释。具体来说,@PostMapping是一个作为快捷方式的组合注释@RequestMapping(method = RequestMethod.POST)。
@RequestMapping:
一般情况下都是用@RequestMapping(method=RequestMethod.),因为@RequestMapping可以直接替代以上两个注解,但是以上两个注解并不能替代@RequestMapping,@RequestMapping相当于以上两个注解的父类!
-
GET,POST 区别
Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。1.根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的。 (1).所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。就是说, 它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。 * 注意:这里安全的含义仅仅是指是非修改信息。 (2).幂等的意味着对同一URL的多个请求应该返回同样的结果。这里我再解释一下幂等这个概念: 2.根据HTTP规范,POST表示可能修改变服务器上的资源的请求。继续引用上面的例子:还是新闻以网站为例, 读者对新闻发表自己的评论应该通过POST实现,因为在评论提交后站点的资源已经不同了,或者说资源被修改了。
POST和GET都是向服务器提交数据,并且都会从服务器获取数据。
区别:
1、传送方式:get通过地址栏传输,post通过报文传输。
2、传送长度:get参数有长度限制(受限于url长度),而post无限制
3、GET和POST还有一个重大区别,简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包
长的说:
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,
你们打开门迎接我”,然后再回头把货送过去。
因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。
但这是一个坑!跳入需谨慎。为什么?
1. GET与POST都有自己的语义,不能随便混用。
2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,
3. 两次包的TCP在验证数据包完整性上,有非常大的优点。
4. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
建议:
1、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;
2、在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式;
案例:一般情况下,登录的时候都是用的POST传输,涉及到密码传输,而页面查询的时候,如文章id查询文章,用get
地址栏的链接为:article.php?id=11,用post查询地址栏链接为:article.php, 不会将传输的数据展现出来。
项目介绍
1.项目背景
- 1.1 建设背景
由于当前社区团购人员变动多,为了方便查看和管理会员和代理以及每个代理发展的下级代理,简化代理分润流程,代理分润公开透明化,业绩统计记录,余额一键提现,会员升级代理、代理等级提升的简单化。于是设计了本系统。 - 1.2 业务系统涉及用户
本系统涉及到代理管理、业务管理、统计分析、系统管理、系统设置
等五个部分,
代理管理
:包括查看代理列表,代理业绩明细,代理分润明细,处理代理升级请求,查看自己的代理申请,余额提现等;
业务管理
:包括新增会员、新增意见反馈、新增订单信息。
统计分析
:统计所有用户的当月销售绩效;
系统管理
:包括查看所有后台用户、角色管理、用户管理、登录日志等功能;
系统设置
:包括设置移动端的轮播图、提现银行设置、意见反馈展示等。
本系统用户涉及公司管理员,运营专员,代理人员,普通会员皆可使用。 - 1.3 业务范围
本系统是用于商品代理的销售分润后台管理系统,用于公司管理员和代理们能够清晰明确的了解自己销售的业绩以及发展的代理给自己分润的详情。方便查看和公司管理。 - 1.4系统架构
2.功能模块介绍
2.1 代理模块
-
2.1.1 查看所有代理信息
实现的功能:1.数据库数据展示 2.CRUD功能 3.批量删除 4.导出Excel 主要使用到的技术:Sql递归查询
-
2.1.2 代理分润管理
管理员可以在代理等级页面对各等级代理的分润率进行编辑修改
-
2.1.3 业绩明细
实现的功能:1.查询 2.导出Excel 主要使用到的技术:Sql递归查询
-
2.1.4 我的升级请求功能介绍
该功能能够查询自己提交的代理升级申请的状态,并且在对应审批人审批前修改申请等级和内容,或者删除该条申请。
在审批人处理完成后将无法进行编辑和删除操作。
管理员进入该页面会跳转到403页面。 -
2.1.5 待处理的升级请求
该功能能够查询自己收到的代理升级请求,用户可以点击同意或者拒绝来改变用户代理升级请求的状态,
点击同意后还会使对应表格中的代理等级发生改变。在用户处理后会使申请的操作状态变为不可操作的状态 -
2.1.6 查询所有用户信息
1、查询所有用户信息
2、模糊查询用户信息
3、将查询信息导出excel表格
2.2 统计模块
查询所有用户每月销售额
1、查看所有用户每月的销售总额
2、模糊查询
3、导出excel表格
4、分页展示
2.3 模拟业务模块
-
2.3.1 余额提现功能
代理用户可以在申请提现页面把自己的收益提现到绑定银行卡 模拟添加订单页面能够模拟代理或会员出售商品,添加一条订单记录,并且根据分润增加自己和上级的余额
-
2.3.2 意见反馈功能
-
2.3.3 代理升级功能
该功能能够提交代理升级请求,在对应的上上级代理同意后发出请求的人的代理等级将会升级,并成为审批人的下级代理。 如果是要求升级到一级代理的话,审批人为后台管理员。 普通会员不能直接成为二级代理,只能先升级为三级代理,但可以直接申请成为一级代理,此时审批人也为后台管理员。 管理员进入该页面会跳转到403页面。
2.4 系统模块
-
2.4.1 后台用户页面:
点击添加按钮输入账号密码可以添加一个新的管理员账号 超级管理员点击删除按钮可以删除一个管理员账户 点击编辑按钮可以打开编辑页面,通过拖拽修改账号角色,只有超级管理员才能修改
-
2.4.2 角色管理页面:
管理员能够添加一个角色; 管理员能够删除一个角色; 管理员能够按照id,或者权限名进行搜索 管理员可以进行批量删除
-
2.4.3 登录日志页面
管理员能够对后台用户登录的日志进行查看,按照时间段查询和导出所有数据为Excel文件 -
2.4.4 轮播图页面
能够新增一个轮播图并上传到alioss云服务器上,删除是一并删除alioss云服务器上的数据,更新时,如果更新的图片名称与alioss云服务器上的不一致时,会将之前的删除掉,然后将新的数据插入到alioss云服务器上。
-
2.4.5 银行信息设置页面
对银行信息进行CRUD -
2.4.6 意见反馈列表
管理员能够对从app传来的意见进行查看,按时间段搜索、处理意见、查看意见详细信息等。
2.5 首页展示
查看访问用户量、代理数量、待处理意见
以及各级代理详情
展示最近7天的天气
以及每天24h的实时气温
查询每月销售总额(根据登录账号)