项目自学记录7
对于枚举类-Stream.of()
public static final InvoiceCatagoryEnum parse(int value) {
return Stream.of(InvoiceCatagoryEnum.values()).filter(e -> e.value == value).findFirst().orElse(null);
}
InvoiceCatagoryEnum.values()得到的是枚举里面的所有枚举实体类的数组,然后Stream.of()将这些数组变为流。
备注:
让我们创建整数流。
Stream<Integer> mystream = Stream.of(10, 12, 14, 16);
假设我们迭代并打印流元素。
mystream.forEach(e -> System.out.println(e));
输出将为10 12 14 16。
orElse就是如果是999这种没有枚举实体类的,就给个null值返回。
@PostConstruct
该注解的方法在整个Bean初始化中的执行顺序:
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的初始化方法)
从数据库里查出JSON数据,然后转map
import com.alibaba.fastjson.JSON;
//从数据库中取出 JSON格式的 额外变量, 然后转为MAP , 再转为 生成OFD所认可的实体类的变量名字
VatInvoiceTdysDomain vatInvoiceTdysDomain = vatInvoiceTdysMapper.selectByFphm(invId);
String tdysDtl = vatInvoiceTdysDomain.getTdysDtl();
Map maps = (Map) JSON.parse(tdysDtl);
BeanUtil.copyList
hutool的beanutil,可以把一个列表转化为另一个列表,而不用写for循环了. 其底层用的是stream,其实也是for循环。
public List<TrainQueryResp> queryAll() {
List<Train> trainList = selectAll();
return BeanUtil.copyToList(trainList, TrainQueryResp.class);
}
DTO,VO的区别
Data Transfer Object和View Object
前端传数据,比如这种
selectByCondition(String userName,LocalDateTime startTime)
参数字段会变多,所以我们可以用DTO,数据传输对象,UserQueryConditionDTO【yonghu chaxun tiaojian DTO]】
后端接口就可以这样写
selectUserByCondition(@RequestBody UserQueryConditionDTO userDto)
VO,返回给视图层【前端】需要用到的对象。
比如前端想要用户的所在部门名称,我们需要一个对象封装带有deptName属性的user
关于多态的反射
B和C两个类都继承自A
A a1=new A(1);
int ran=1;
if(ran==1){
a1=new B(2);
}else{
a1=new C(3);
}
System.out.println(a1.getClass().getSimpleName()); //输出B
可以发现A这个类被赋值后,反射得到的类名是B。
SQL使用[CDATA[ ]]来代替转义字符大于小于号
[]中的东西不被xml解析,尽量缩小 的范围
mybatis中的
使用工厂模式+策略模式对版式文件生成进行优化
接口+2个实现类,就是天然的实现了策略模式。而用哪个策略,取决于工厂生成的对象是什么。
接口就是抽象策略类,接口的实现类是具体实现类。工厂生成对象写在Client类里,是上下文。
首先是抽象接口:
@FunctionalInterface
public interface IVatCreateFormatFileService {
/**
* 生成发票版式文件
* @param fph 发票号
* @param sellerCode 销货单位编码
*/
void createFormatFile(String fph, String sellerCode);
}
有pdf和ofd两个类实现了该接口
@Service("vatCreateOfdFormatFileServiceImpl")
public class VatCreateQdOfdServiceImpl implements IVatCreateFormatFileService {
@Service("vatCreatePdfFormatFileServiceImpl")
public class VatLocalPdfServiceImpl implements IVatCreateFormatFileService
然后是工厂,工厂的目的是生成对象Bean,而且是以强配置的形式
@Component
public class FormatFileCreateFactory {
private FormatFileCreateFactory() {
}
@Resource
private IVatInvoiceService iVatInvoiceService;
@Resource
private Environment environment;
private String getFormatFileType(int invoiceCatagory) {
return environment.getProperty(String.format("zbiti.invoice-%d.formatfile.type", invoiceCatagory));
}
public IVatCreateFormatFileService getFormatFileBean(String invId) {
//根据发票号,得到是电票 [invoiceCategory 的值为3或4] 还是 数电票[invoiceCategpry的值为5或6]
Integer invoiceCatagory = iVatInvoiceService.selectById(invId).getInvoiceCatagory();
//从配置文件中去取 得到formatFileType为pdf 或 ofd
String formatFileType = getFormatFileType(invoiceCatagory);
if (null == formatFileType) {
return null;
}
//规格化一下,formatFileType1Big的值为 Pdf 或 Ofd
String formatFileType1Big = formatFileType.substring(0, 1).toUpperCase() + formatFileType.substring(1).toLowerCase();
return SpringUtils.getBean("vatCreate" + formatFileType1Big +"FormatFileServiceImpl");
}
}
最后是供别的地方调用的客户端Client
@Service
public final class FormatFileCreateClient {
@Autowired
private FormatFileCreateFactory factory;
public void createFormatFile(String invId, String sellerCode){
//通过工厂得到Bean对象
IVatCreateFormatFileService formatFileBean = factory.getFormatFileBean(invId);
if (null != formatFileBean) {
//Bean对象调用接口,生成版式文件
formatFileBean.createFormatFile(invId, sellerCode);
}
}
}
其他类直接注入Client就可以直接调用了
@Resource
private FormatFileCreateClient formatFileCreateClient;
formatFileCreateClient.createFormatFile(invId, sellerCode)
接口中可以进行方法的实现,但要用default
Java 8 开始是可以有方法实现的,可以在接口中添加默认方法和静态方法
在 Java 8 之前,比如要在一个接口中添加一个抽象方法,那所有的接口实现类都要去实现这个方法,不然就会编译错误,而某些实现类根本就不需要实现这个方法也被迫要写一个空实现,改动会非常大。
所以,接口默认方法就是为了解决这个问题,只要在一个接口添加了一个默认方法,所有的实现类就自动继承,不需要改动任何实现类,也不会影响业务,爽歪歪。
另外,接口默认方法可以被接口实现类重写。
Java8的并行流和foreach
strList.parallelStream().forEach(each -> {
integerList.add(Integer.parseInt(each));
});
这种代码是线程不安全的,如果integerList是ArrayList就是线程不安全的。
200—普通for循环数组
200—stream流for循环数组
197—parallelStream流for循环数组—会丢失数据
sql的if的有意思的地方
这样写,直接在sql里做判断。
freemarker
在12306项目里用于制作代码生成器模板,在黑马头条项目里用于生成静态的文章页面,然后存在MinIo里
读环境信息,并用String.format
# 版式文件类型 ofd,xml,pdf
# 3 电子普票
invoice-3:
formatfile:
type: pdf
# 4 电子专票
invoice-4:
formatfile:
type: pdf
# 5 数电专票
invoice-5:
formatfile:
CargoTransportation-type: ofd #交通运输
Common-type: ofd # 普通票
Construction-type: ofd # 建筑服务票
RealEstateBusinessLeasing-type: ofd # 不动产票
Photovoltaic-type: ofd #光伏票
Cigarette-type: ofd #卷烟
# 6 数电普票
invoice-6:
formatfile:
CargoTransportation-type: ofd #交通运输
Common-type: ofd # 普通票
Construction-type: ofd # 建筑服务票
RealEstateBusinessLeasing-type: ofd # 不动产票
Photovoltaic-type: ofd #光伏票
Cigarette-type: ofd #卷烟
在销项中就是
private String getFormatFileType(int invoiceCatagory,String specialTdys) {
return environment.getProperty(String.format("zbiti.invoice-%d.formatfile.%s-type", invoiceCatagory,StringUtils.nvl(specialTdys,"")));
}
分布式文件系统
根据枚举里的一个属性值,去查另一个属性值
本来以为要写for循环,结果发现hutool提供了工具类
第一个参数,你要得到哪列。第二个参数,你要通过哪列去查,第三个参数,第二个参数所对应的值。
BigDecimal priceRate = EnumUtil.getFieldBy(TrainTypeEnum::getPriceRate, TrainTypeEnum::getCode, trainType);
public enum TrainTypeEnum {
G("G", "高铁", new BigDecimal("1.2")),
D("D", "动车", new BigDecimal("1")),
K("K", "快速", new BigDecimal("0.8"));
private String code;
private String desc;
/**
* 票价比例,例:1.1,则票价 = 1.1 * 每公里单价(SeatTypeEnum.price) * 公里(station.km)
*/
private BigDecimal priceRate;
}
比如说trainType=”G”,则priceRate=1.2
bigDecimal的乘法,并且四舍五入
BigDecimal ydzPrice = sumKM.multiply(SeatTypeEnum.YDZ.getPrice()).multiply(priceRate).setScale(2, RoundingMode.HALF_UP);
都是几个bigDecimal相互乘,最后调整一下小数位。
一些操作,应该先清空,再重新赋值。
根据某个属性,得到枚举值
SeatTypeEnum seatTypeEnum = EnumUtil.getBy(SeatTypeEnum::getCode, seatTypeCode);
根据getCode函数的值为seatTypeCode得到枚举值