java8语法
类和对象
变量
一个类可以包含三种变量:局部变量
,成员变量
,类变量
public class Dog{ |
构造方法
用来初始化对象,一个对象必须要有一个构造方法
public class Pub{ |
创建对象
- 声明
- 实例化
- 初始化属性
public class Car{ |
访问成员变量
public class Car{ |
继承 重写
继承
公共类
public class Animal { |
子类
public class Penguin extends Animal { |
如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
多继承接口
public interface A { |
子类调用父类方法
class Dog extends Animal { |
重写
子类对父类允许访问的方法进行重新编写, 即外壳不变,核心重写!
例:
class Animal{ |
多态 抽象类
多态
同一个行为不同的实例进行不同操作
多态例子:英雄分为 近战,射手 都可以进行杀敌行为
接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口支持多继承
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
例:
interface Animal { |
实现类
/* 文件名 : MammalInt.java */ |
数据类型
基本类型
- byte/8
- char/16
- short/16
- int/32
- float/32
- long/64
- double/64
- boolean/~
装箱拆箱
Integer x = 2;//装箱 |
引用类型
对象,数组都是引用类型,引用类型指向一个对象,状态可以保存
例子:Site site = new Site(“Runoob”)。
常量
final double PI = 3.14 |
类型转换
自动类型转换
必须满足规则
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double
char c1 = 'a'; |
强制转换
语法:
(type)value type
int i = 123;
byte b = (byte)i
字符串
常用转义字符
String name = 'runoob.com' |
API
- concat 连接
- compareTo 比较是否相等 返回0
- endsWith 结尾
- indexOf 返回第一次出现索引
- lastIndexOf
数组
基础
声明
dateType []arrayName;
创建
1. int []array = new int[size]
2. int []array = {1,2,3...};遍历
int []array = {1,2,3,4,5,6};
for(int i = 0;i < array.length;i++){
}
// 加强for
for(int item:array){
System.out.println(item)
}数组作为函数参数,和函数返回数组
public static void printArray(int[] array){
}
public void int[] reverse(int[] array){
int[] result = new int[array.length];
return result;
}
多维数组
int a[][] = new int[2][3]
API
修饰符
访问修饰符
- default 默认
- private 私有
- public 共有
- protected 同一包内可见,不能修饰外部类(接口)
非访问修饰符
- static 静态方法,静态变量
- final 保持不变
- abstract 抽象类, 声明抽象类的唯一目的是为了将来对该类进行扩充 。
- synchronized 同一时间只能被一个线程访问
Number,Math,Date
Number,Math
Integer x = 7 // 创建Number |
Date
Date date = new Date() |
Calendar
Calendar c1 = Calendar.getInstance(); |
枚举 集合 泛型
概念
枚举:参考文章 地址
集合
Collection 接口
List 接口
Set 接口
Map 键值队
Enumeration
使用实例
List<String> list = new ArrayList<String>();// 声明数组集合 |
Springboot
理解IOC 依赖注入
IOC
IOC
- 控制反转,在传统SE
阶段如果A
类需要使用到B
类,需要在A
的内部new
一个B
的实例出来,进而控制对象内部。而IOC
专门有一个容器
来创建这些依赖的对象,然后控制要交给A
类哪些依赖。至于为什么叫反转
,因为传统的获取依赖对象是对象主动去控制
,而IOC
的控制交给了容器,容器帮忙创建注入依赖对象,此时A
对象只是被动的接收依赖的注入
。
IOC
容器控制对象,主要控制外部资源获取- 容器帮忙创建并注入依赖对象,程序被动接受
依赖对象
DI
DI
-依赖注入,容器将某个依赖关系注入到组件中去。依赖注入组件重用
的频率,依赖注入是IOC
思想最好的表现形式。
谁依赖谁:
应用程序
依赖于IOC
容器**为什么要使用
DI
**:应用程序需要IOC
提供提供对象外部资源谁注入谁:
IOC
注入应用程序的对象
我的理解
IOC
的出现解决了传统java开发应用程序,对象与对象之间耦合性,A
需要B
,A
就要主动创建B
对象,进行控制,这样A
和B
就产生了依赖,而且是紧密耦合性。IOC
则不同,我们只需要吧需要的依赖在容器
中创建好,然后要的时候由容器
进行注入,A
对象根本不用去管B
以及更多依赖的问题,只需要交给容器处理,二者协作进行即可。
springboot阶段学习
版本号
2.2.1 RELEASE
2 主版本 |
运行springboot项目
在官网中创建项目然后用idea打开,或者直接创建springboot项目
- 打开 idea -> create project |
修改默认端口
resources/application.properties 下添加
server.port = 端口号
编写第一个接口
创建api.v1/api.v2
文件夹管理api
,然后创建一个控制器类.
分为三步 |
配置热重启
安装devtools
,然后配置idea
- 搜索 Compiler
- 勾选 build project automatically
常见注解
请求method限定
// 全方法都支持
// method传递支持的方法Springboot 提供的 简化注解
@RestController
意思就是@Controller
@ResponseBody
统一路径管理
|
深刻理解springboot
开闭原则-OCP
软件,函数,类是扩展开发的,修改是封闭的。举例说明:修改业务最好是通过新增业务模块进行处理,API的v1
和v2
版本的使用。
面向抽象编程
interface
,设计模式:工厂模式
,IOC/DI
如果是具体类的话,修改是灾难性的。面向抽象的话,只需要调方法即可。真正的目的就是实现开闭原则从而达到代码可维护性。
springboot和springframework的区别
springboot 借助springframework开发的
Spring、SpringMVC与SpringBoot的关系与区别
SSM
Spring + SpringMVC + MyBatis
Spring 全称是 Spring Framework
- SpringBoot 是 Spring Framework 的应用
什么是SpringBoot核心优势-自动配置
Springboot意义性(死记)
OCP -> IOC
IOC实现: 容器,把控制类加入 容器 ,在你需要时把对象注入你所需要的代码里去
抽象意义:控制权交给用户
如何将对象加入容器,并注入
xml
注解
stereotype annotations
模式注解,两步
加入容器
扫描加入容器注解
加入服务层容器
使用的时候,注入,已经实例化
1.成员变量注入
private Diana diana
2.推荐注入方式:构造器,不需要加入注解
private final Diana diana;
public BannerController(Diana diana) {
this.diana = diana;
}
- `@Component` 最基础的模式注解
- 把一个组件/类/bean加入到容器中
> 以下都是以 `@Component` 为基础的衍生注解
- `@Service` 标明是种服务
- `@Controller` 标明是个控制器
- `@RestController` 标明是个restful 的 控制器
- `@Repository` 标明是个仓储
- `@Configuration` 更灵活的方式 把一组bean加入到容器里
- 跟上面的有些不同
- 具体参考[@Configuration 注解介绍](
##### 实例化时机和延时实例化
> 如何允许 未添加 `@Component` 注解的类 为空值@Autowired(required = false)
private Diana diana;
> IOC 对象实例化注入时机
- 在Spring启动的时候就开始 对象的实例化 并注入
- 这是一个默认的机制 **立即/提前 实例化**
- 延迟实例化 `@Lazy`
##### @Autowired 类型注入方式
预设环境,多个类实现接口,并且加入到容器,使用`@Autowired`注入会加载哪一个实现
<h2>被动注入</h2>
- **bytype** 默认注入形式
- 根据类型推断到底应该注入谁
- 比如上面的如果注入 ISkill 就会去所有加入到容器里的bean去寻找实现ISkill的类 加入进来@Autowired
private ISkill iSkill;
- **如果仅仅有一个实现类 Diana 那就会加载它**
- **如果有多个bean时候,字段找不到对应的`bean`, spring就不知道到底该注入谁。就会报错**
- **byname**
<h2>主动注入</h2>
强制设置注入的`value`
```java
@Autowired
@Qualifier("irelia")
private ISkill iSkill;
// 这样它就会 按你要求注入 Irelia
如何应对变化
制定一个 interface ,然后多个类实现同一个 interface.
- 策略模式
- 只能选一个策略
一个类,属性 解决变化
// 比如你只有一个实现类 Diana ,但你想打印 irelia
package com.lin.missyou.sample.hero;
import com.lin.missyou.sample.ISkill;
import org.springframework.stereotype.Component;
public class Diana implements ISkill {
private String skillName = "Irelia"
public Diana() { System.out.println("hello,Diana"); }
public void q(){ System.out.println("Diana Q"); }
public void w(){ System.out.println("Diana W"); }
public void e(){ System.out.println("Diana E"); }
public void r(){ System.out.println(this.skillName + "r"); }
}- 像这样实际上是不好的,因为在代码里了。
- 如果你要用这样方式: 应该用读取配置的方式 这样才不违背 OCP 原则
- 比如 spring boot 默认 8080 端口,你可以通过修改配置文件 让他启动在其他端口
- 这种方式不具备 扩展性,如果未来你想添加新的属性,就要改这个类了,也不够灵活。除非你保证以后这个类不再变了
@configuration注解使用方法
新建 HeroConfiguration.java
在 HeroConfiguration上 使用
@Configuration
对注入的对象 Camille 使用
@Bean
,返回一个bean
实例
@Configuration |
- 控制器类里稍微修改下
@RestController |
优势
如果我们的 hero 类 新增了属性那么如何初始化
@Component
是无法做到把 类的属性进行初始化的@Configuration
则可以@Configuration
还能同时初始化多个 bean
Camille.java
- 新增了 name / age 字段 并在构造器里赋值
public class Camille implements ISkill { |
HeroConfiguration.java 里只需要这样就可以实现
- 即使你想调用无参构造器 ,也可用通过 setName / setAge 在 return camille
|
springboot扫描注解
默认是与程序主入口文件夹下
@ComponentScan("path")
手动指定你想要加载其他位置的包
- 如果手动指定的路径 已经在默认路径里会标红
- 新增的扫描位置是不影响原来的扫描位置,是可以叠加的
@SpringBootApplication |
策略模式实现的几种方式
byname 方式
切换 bean的 name
private Iskill diana
@Qualifier
指定 bean多个bean情况下
private Isskill isskill
有选择的只注入一个 bean 注释掉某个 bean的 @Component
@Primary
- 如果同时 Diana 和 Irelia 同时加上了 @Component
- 如果定义的时候是 ISkill iskill 那么会报错
- 可以想让 Diana 生效则 额外添加
@Primary
public class Diana implements ISkill {
...
}
条件注解
配置类
|
自定义条件注解
@Conditional
+ 实现Condition 接口的元类
分别注释掉 Diana 和 Irelia 里的
@Component
修改 HeroConfiguration.java
@Configuration
public class HeroConfiguration {
@Bean
@Conditional(DianaCondition.class)
public ISkill diana(){
return new Diana();
}
@Bean
@Conditional(IreliaCondition.class)
public ISkill irelia(){
return new Irelia();
}
}DianaCondition.java / IreliaCondition.java
// 注意 java 很多类都有 Condition 这里引入 spring framework的
import org.springframework.context.annotation.Condition;
public class DianaCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 判断条件
return true;
}
}
public class IreliaCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 判断条件
return false;
}
}
二次封装springboot框架机制
异常处理概念
统一异常捕获
|
异常分类
CheckedException
受检异常 (可以处理,比如是A类调用B类的方法,但是B类没有方法)
- 编译阶段进行处理,否则编译通不过.
- 必须程序里主动处理
RuntimeException
运行时异常(不能处理,比如数据库记录查询结果为空)
- 可以不处理
对于web如果有全局的异常处理
- 可以不区分
全局异常处理步骤
处理的异常分为已知异常或者未知异常
1. 定义HttpException类
,继承RuntimeException
public class HttpException extends RuntimeException{ |
2. 基类子异常,比如NotFoundException
public class NotFoundException extends HttpException{ |
3. 定义统一异常返回格式类 ,构造函数初始化属性,并且一定要有getter
方法,否则无法访问
public class UnifyException { |
4. 全局捕捉异常类GlobalException
,分为已知异常
和未知异常
@ControllerAdvice
捕获异常注解
@ExceptionHandler(value = Exception.class)
捕获到进行处理的注解
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
设置状态码
可以通过req
获取method
,url
,
已知异常: 处理
Exception
,设置HttpstatusCode为500,然后返回UnifyException
未知异常: 使用
ResponseEntity<UnifyException>
定义一个泛型,然后返回这个泛型,需要传入三个参数,message
,header
,HttpStatusCode
public class GlobalException {
// 加载配置文件关联的类 处理message
private ExceptionCodeConfiguration codeConfiguration;
// 未知异常
public UnifyException handleException(HttpServletRequest req,Exception e){
// 当有checkedException异常抛出,加上这两个方法,会在全局捕捉到异常,并运行方法
String url = req.getRequestURI();
String method =req.getMethod();
UnifyException message = new UnifyException(999,"服务器异常",method+" "+url);
// 返回对象序列化
return message;
}
// 已知异常
public ResponseEntity<UnifyException> handleHttpException(HttpServletRequest req, HttpException e){
String url = req.getRequestURI();
String method = req.getMethod();
UnifyException message = new UnifyException(e.getCode(),codeConfiguration.getMessage(e.getCode()),method+" "+url);
// 设置已知异常的Httpstatus 利用泛型设置
//ResponseEntity<UnifyResponse> r = new ResponseEntity<>(message,header,httpStatus);
// 处理返回的 header httpstatus
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
HttpStatus httpStatus = HttpStatus.resolve(e.getHttpstatusCode());
ResponseEntity<UnifyException> r = new ResponseEntity<>(message,header,httpStatus);
return r;
}
}
自定义错误码,统一返回格式
首先定义一个文件管理code
码对应的message
,俗称错误码清单
lin.codes[10000] = 通用异常 |
然后,定义一个configuration
类对配置文件进行关联,将codes
看做是Map
数据结构,然后定义方法获取到message
@ConfigurationProperties(prefix = “lin”) 配置前缀
@PropertySource(value = “classpath:config/exception-code.properties”) 加载配置文件路径
@Component
|
然后将之前写死的message
替换成方法即可
构建校验层
获取参数
@PathVariable
获取路径中的参数 param
@RequestParam
获取query
参数
@RequestHeader
@RequestBody
|
获取POST
请求的body
定义一个DTO
类,数据传输类
public class PersonDTO { |
然后用@RequestBody PersonDTO person
获取对象
|
使用lomBok
生成
getter,setter
@Getter
@Setter
生辰有参数构造函数和无参构造函数
@AllArgsConstructor
,@NoArgsConstructor
@AllArgsConstructor 全参数构造函数
@NoArgsConstructor 无参数构造函数生成必填参数的构造函数
@RequiredArgsConstructor
public class PersonDTO {
private String name;
private Integer age;
}
// 生成了必填name的构造函数创建对象实例化
import lombok.Builder;
public class PersonDTO {
private String name;
private Integer age;
}创建对象的时候就可以这样
PersonDTO.builder() |
@Builder
的坑- 一旦加上这个注解,就不能用构造器的方式 实例化了
- 即使你加了 @Getter/@Setter
也不能这样了
PersonDTO p = new PersonDTO();
p.setName("abc");
p.setAge(19)因为 @Builder 之后会生成 私有的无参构造函数
你只能在手动添加
@NoArgsConstructor
注解生成共有的无参构造器,或者手动写一个 共有的构造器
public class PersonDTO {
private String name;
private Integer age;
}对象实例化最佳用法
使用
@builder
实例化对象,必须要添加@Getter
,否则无法序列化。
public PersonDTO test5( Integer id,
String name,
PersonDTO person){
PersonDTO dto = PersonDTO.builder()
.name("abc")
.age(19)
.build();
return dto;
}
使用校验器注解校验
- 验证简单参数,在
class
头部添加validated
@Range
Max
Min
public PersonDTO test( Integer id){ |
- 验证
DTO
参数,然后再方法前面加上@Validated
public class PersonDTO { |
级联校验:如果
DTO
中还有一个对象需要校验,必须要加上@Valid
注解关联
private SchoolDTO schoolDTO;
public class SchoolDTO {
String schoolName;
}
编写自定义校验注解
- 自定义注解写法 ,定义注解class
// 注释文档加入 |
编写自定义校验注解的关联类,继承
ConstraintValidator<PasswordEqual, PersonDTO>
ConstraintValidator<> 是一个泛型接口
接受两个参数第一个是 自定义注解类型 PasswordEqual 第二个是 这个自定义注解 PasswordEqual 修饰的 目标类型,`你要校验哪个DTO 你就加在谁身上`(PersonDTO)
实现
isValid 方法
具体的校验逻辑定义关联类
public class PasswordValidator implements ConstraintValidator<PasswordEqual, PersonDTO> {
public boolean isValid(PersonDTO personDTO, ConstraintValidatorContext constraintValidatorContext) {
String password1 = personDTO.getPassword1();
String passwword2 = personDTO.getPassword2();
return password1.equals(passwword2);
}
}将校验注解添加上即可
给注解添加参数,然后再关联类获取注解信息
int min() default 4;
int max() default 8;
重写方法 获取到注解的参数 |
返回错误信息给前端
改造全局异常处理类 校验异常
MethodArgumentNotValidException
一个是body
校验,一个是url
传递// 参数异常
public UnifyException hanldeMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e){
String url = req.getRequestURI();
String method = req.getMethod();
// 拿到所有的验证异常
List<ObjectError> errors = e.getBindingResult().getAllErrors();
String messages = format(errors);
return new UnifyException(10001,messages,method+" "+url);
}
private String format(List<ObjectError> errors){
// 获取错误信息数组 将数组拼接成字符串
StringBuffer errorMsg = new StringBuffer();
errors.forEach(error-> errorMsg.append(error.getDefaultMessage()).append(";"));;
return errorMsg.toString();
}
public UnifyException handlerConstrainException(HttpServletRequest req, ConstraintViolationException e) {
String requestUrl = req.getRequestURI();
String method = req.getMethod();
//这里如果有多个错误 是拼接好的,但是如果需要特殊处理,就不能用它了
String message = e.getMessage();
/*
// 自定义错误信息时
for (ConstraintViolation error:e.getConstraintViolations()) {
ConstraintViolation a = error;
}
*/
return new UnifyException(10001,message,method + " " + requestUrl);
}至此,异常处理和验证信息层全部封装完毕,只需要简单的添加注解或者主动抛出异常类即可使用。
返回成功时
正常返回时也应该有状态码显示
public Map<String, Object> getByName( String name){
Banner banner = bannerService.getByName(name);
if(banner == null){
throw new NotFoundException(30005);
}
Map<String, Object>results = new HashMap<String, Object>();
results.put("code",0);
results.put("message","返回成功");
results.put("data",banner);
return results;
}
JPA
业务层
一般都是用
接口
来进行层与层的调用,首先定义BannerService
接口,然后实现接口方法,然后将接口加入容器@Service
,但是这样非常繁琐
,可以直接实现类
启动命令启动我们的程序(不同的环境)
spring: |
配置文件的区分,在全部的环境下application.yml
,在开发环境application-dev.yml
,在上生产环境application-prod.yml
服务器上实际是一个 jar包,没有 idea工具,所以要通过命令启动
执行打包命令
mvn clean package
就会在 target目录下生成一个 jar文件通过 命令
java -jar xxx.jar --spring.profiles.active=dev
指定读取哪个配置文件
新建表
安装好部分依赖后,配置application.yml
,url
中指定数据库和端口号和配置字符,使用jpa
,一般都是这样配置
spring: |
然后新建模型类Banner
,一定要打上@Entity
实体注解,然后指定主键
|
表与表的关系
一对一: 人 身份证 |
一对一
- 一张表字段过多,有意识的拆成多个表
- 设计一对一考虑:
- 查询效率,避免一个表字段太多
- 业务的角度,不同的业务归类在不同的表里
一对多
- 最简单的一种关系
多对多
- 至少三张表才能表示 多对多
- student (sid,name)
- teacher (tid,name)
- student_teacher (id,sid,tid) 老师学生关系表
- 多对多复杂性:
- 难点在于 表实际上有很多,每个表和其他表可能也有关系,这样问题就会变得复杂了
- 第三张表是不是一个毫无意义的表?
- 比如student_teacher 它只是用来记录关系 ,这就是无实际业务意义的表
- 有意义的第三张表
用户-优惠券
user_coupon(id,uid,cid,status,order_id,update_time) 有 业务字段- status 为 优惠券状态
- 只有 status = 2的时候代表 使用了优惠券,order_id 记录是那笔订单
- 这样保存在第三张表的 status,order_id 就是记录 实际的业务意义
设计数据库的步骤
- 把业务对象抽离出来,比如:
Coupon优惠券
,banner
,order
- 思考对象与对象之间的关系,比如
外键
- 细化:
字段限制
,长度
,小数点
,唯一索引
解决性能问题:
- 表的记录不能过多
- 建立索引,水平垂直分表
- 加入缓存机制
层与层之间的关系(重点)
在编写业务之前,理清楚各层之间的关系
- model 层 对象实体层,我们最终要返回的结果基本上都是以model对象的形式返回的。例如在
service
层定义接口public interface BannerService {
Banner getByName(String name);
}
或者实现接口
public Banner getByName(String name) {
return bannerRepository.findOneByName(name);
} - service层 业务层,通常不能将处理业务的方法定义在
控制器层
,都是定义在service
,然后标准的定义方法就是先定义接口
,再实现接口,当要操作数据库时,调用repository
层的JPA
接口
接口实现类,记得要导出@service
|
- controller层 控制层,通常接口定义在这一层,并且这一层负责调用
service
层方法,进行相关业务,然后返回结果。
public Banner getByName( String name){
Banner banner = bannerService.getByName(name);
return banner;
}
编写JPA业务的常用知识
设置关联表
加入一个BannerItem
模型,一个banner对应多个BannerItem
|
但是发现这样运行程序,JPA
会生成三张表,我们并不需要第三张表
单向一对多
|
建立repository层
建立操作数据库的仓储层,新建repository
包,再新建接口,这里不需要实现接口,只需要引入方法自动生成sql语句,service
层调用
public interface BannerRepository extends JpaRepository<Banner,Long> { |
执行业务(调用repository接口)
然后在service
层调用
|
最后在controller
层调用业务层方法,返回数据.
设置sql打印在控制台
>jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
show_sql:true
format_sql:true
这里会发现默认是懒加载
,只有当你去展开banner-item
时才去查询另一张表。
设置急加载模式:@OneToMany(fetch = FetchType.EAGER)
双向一对多配置
有时候,我们需要在查询bannerItem
时同时查询到banner
导航,这里就需要设置双向一对多关系,分为两个方:一方
,多方
设置
BannerItem
多对一关系,这里是多方,记住双向一对多关系中@JoinColumn
一定是打在多端的
private Banner banner;设置
Banner
一方的mappedBy
,值是多方导航属性的名字,也就是上面定义的banner
private List<BannerItem> items;这里可能要注释掉定义的
bannerId
,如果要显示添加
bannerId
字段,就要设置外键private Long bannerId;
private Banner banner;BannerItem
public class BannerItem {
private long id;
private String img;
private String keyword;
private String type;
private String name;
private Long bannerId;
private Banner banner;
}Banner
public class Banner {
private long id;
private String name;
private String description;
private String img;
private String title;
// 双向一对多关系 查询结果中会有bannerItem数组
private List<BannerItem> items;
}
多对多配置
- 单向多对多关系
创建两个实体类,主题和SPU的多对多关系,只需要在一方设置注解
Spu.java
|
Theme.java
|
此时生成三个表
spu |
我们不想要生成的关系表叫做 theme_spu_list 而是 theme_spu 才对
而且 theme_spu_list 里的字段 spu_list_id 应该是 spu_id 才对
这里我使用到@JoinTable
这个注解,指定外键和第二个外键名称和第三张表名
|
这样生成的关系表就是 theme_spu(theme_id,spu_id)
- 双向多对多关系
双向多对多关系需要配置被维护端
,哪一方有mappedBy
那一方就是 被维护端
Spu.java
@Entity |
Theme.java
@Entity |
针对我们这个 spu / theme 关系维护端和被维护端可以相互颠倒。
针对查询来说,无论谁是 关系维护端还是被维护端都没有关系。
禁止JPA生成物理外键
|
但这是一个过时废用的注解,新注解有bug
使用idea
反向生成Entity
模型类
编写业务
banner(/banenr)
创建一对多关系
首先反向生成模型,然后简化,之后编写service
层实现方法,然后在service
层操作数据库调用repository
层,最后在控制器层调用实现的方法返回数据即可
优化返回的三个时间
首先提取基类baseEntity
,然后模型继承该类,处理事件问题,一定要打上@MappedSuperclass
这个注解,表示映射类,这样才能显示正确时间
|
然后访问之前写过的Banner
接口
配置默认的jachson优化返回结果
jackson: |
然后控制某个字段不序列化,打上@Jonignore
即可
|
然后返回结果
附加where的查询语句
|
Theme
spu
详情(/{id}/detail)
spu的简单的单表查询就常规写法即可,创建service
层,然后调用repositrty
层,定义返回的结果是Spu
即可
后面需要配置一下导航属性 查看detail-img
和spu_img
// 配置导航关系 |
序列化和反序列化(改变spec返回格式)
然后会碰到一个问题,我们读取
spec
时需要一个json
数组对象,但是数据库中存放的时字符串
然后读取的时候要将对象转换成字符串我们要做的就是将json字符串 转为json对象 ,json字符串分为List 和单体的 Map
编写ListAndjson
和MapAndjson
工具类
单体MapAndjson
继承AttributeConverter
接口,实现两个方法 然后传入的参数是 希望序列化的实体属性类型
,数据库json类型
这里json我们通常写成String
这里使用到了序列化工具ObjectMapper
|
同理将json
字符串返回数组类型json格式
public class ListAndjson implements AttributeConverter<List<Object>,String> { |
使用这两个序列化类在实体属性上打上注解@convert(@converter=ListAndjson.class)
但是失去了业务能力 就是不能调用model下的方法
获取最新商品列表(/latest)
直接返回全部latest
列表,在spuService
定义方法调用getAll()
即可返回,但是这种方式太捞了,要增加分页
和排序
的功能。
vo层自定义返回数据格式(简化数据)
- 单个vo对象
|
然后拷贝属性,返回vo
格式的数据,做这一步的目的是为了简化这样的写法
vo.setTitle(spu.getTitle());
Spu spu = this.spuService.getSpuById(id); |
- 一组vo对象
如果是一个数组,需要借助一个库dozer-core
// 引入 mapper |
分页
传统web端
: 分页参数一般为 pageSize
,page
移动端
: 分页参数一般为start
,count
具体实现思路:
- 设置默认的参数
- 创建分页业务对象(bo层) 将
start
和count
转化成page
和count
service
传入page
和count
,利用JPA查询出pageable
对象,然后返回- 利用
dozer-core
创建pagingDozerVo
和pagingVo
层简化数据对象
getLatestSpuList( Integer start, Integer count) |
pageCounter
搭配编写的CommonUtil
方法类处理接收的参数
|
public class CommonUtil { |
service
查询分页数据,这里返回的数据是Page<>
泛型,查询API:findAll(pageable)
public Page<Spu> getLatestPagingSpu(Integer pageNum, Integer size){ |
简化数据层pagingVo
,封装简化数据层和初始化数据PagingDozer
这两个类均是泛型类
|
封装过后的vo
层
public class PagingDozerVo<T,K> extends PaggingVo { |
最后controller
依次调用
|
根据分类id获取商品
前面铺垫好了方法,就比之前好写多了。这个接口同样是返回分页数据,需要通过传递参数isroot
判断返回数据是返回一级分类还是二级分类
jpa
的语法
/* |
service
层
public Page<Spu> getListByCid(Integer pageNum,Integer size,Long cid,Boolean isroot){ |
|
关键词搜索商品(/search)
这里就相对简单的多,只需要书写正确的Jpa
方法即可
// 传入keyword |
分类(/category)
sku
数据表设计
SPU(有多种规格 多个sku) |