当前位置: 首页 > news >正文

韩国购物网站永嘉网站优化

韩国购物网站,永嘉网站优化,北京建设部网站,二次感染即将大爆发作者#xff1a;小凯 沉淀、分享、成长#xff0c;让自己和他人都能有所收获#xff01; 本文的宗旨在于通过简单干净实践的方式教会读者#xff0c;使用 SpringBoot 配置 MyBatis 并完成对插入、批量插入、修改、查询以及注解事务和编程事务的使用#xff0c;通过扩展插件…作者小凯 沉淀、分享、成长让自己和他人都能有所收获 本文的宗旨在于通过简单干净实践的方式教会读者使用 SpringBoot 配置 MyBatis 并完成对插入、批量插入、修改、查询以及注解事务和编程事务的使用通过扩展插件开发对指定字段进行加解处理。 此外本文也通过此案例渗透讲解 DDD 模型中的聚合对象、实体对象和值对象在领域模型中的实践。 一、案例背景 说一千道一万写的教程得简单还好看 为了更好的把 MyBatis 常用的各项功能体现的清晰明了这里设定了公司雇员和对应薪酬关系的一个开发场景。首先雇员员工和对应的薪资待遇是一个1v1的关系。之后薪资表与调薪表是一个1vn的关系。每次晋升、普调都会有一条对应的调薪记录。最后有了这样3个表我们就可以很好的完成员工的插入、批量插入和事务操作调薪。 二、领域模型模型定义理解模型概念和设计原则。此场景的业务用于对指定的用户进行晋升加薪调幅但因为加薪会需要操作3个表包括雇员表 - 修改个人Title、薪资表 - 修改薪酬、调薪记录表 - 每一次加薪都写一条记录。 1. model 1.1 值对象 public enum EmployeePostVO {T1(T-1, 初级工程师),T2(T-2, 初级工程师),T3(T-3, 中级工程师),T4(T-4, 中级工程师),T5(T-5, 高级工程师),T6(T-6, 高级工程师),T7(T-7, 架构师),T8(T-8, 架构师);private final String code;private final String desc;// 省略部分}当一个实体对象中的一个值是有多个范围时候则需要定义出值对象。由于此类的值对象更贴近于当前的场景业务所以一般不会被定义为共用的枚举。如此此类值范围都会被定义为值对象。 1.2 实体对象 Data Builder AllArgsConstructor NoArgsConstructor public class EmployeeEntity {/** 雇员级别 */private EmployeePostVO employeeLevel;/** 雇员岗位Title */private EmployeePostVO employeeTitle;}Data Builder AllArgsConstructor NoArgsConstructor public class EmployeeSalaryAdjustEntity {/** 总额调薪 */private BigDecimal adjustTotalAmount;/** 基础调薪 */private BigDecimal adjustBaseAmount;/** 绩效调薪 */private BigDecimal adjustMeritAmount;}实体对象是对数据库对象的抽象大多数时候是 1:1 的关系结构在一些复杂的模型场景中会是1:n的结构。 1.3 聚合对象 Data Builder AllArgsConstructor NoArgsConstructor public class AdjustSalaryApplyOrderAggregate {/** 雇员编号 */private String employeeNumber;/** 调薪单号 */private String orderId;/** 雇员实体 */private EmployeeEntity employeeEntity;/** 雇员调薪实体 */private EmployeeSalaryAdjustEntity employeeSalaryAdjustEntity;}聚合对象是对实体对象和值对象的封装代表着一类业务的聚合。通常是作为 service 服务层中入参出现。 2. repository public interface ISalaryAdjustRepository {String adjustSalary(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate);}仓储在 DDD 中的设计是一种依赖倒置关系由 domain 定义接口之后由引入 domain 包的基层层 infrastructure 实现功能。此外因为是依赖倒置所以天然的隔离了 PO 数据库持久化对象不会被对外使用。这个设计是非常巧妙的。当我们从结构上定义了原则就不会有人乱引用对象了。 3. service public interface ISalaryAdjustApplyService {String execSalaryAdjust(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate);}当前场景简单所以不需要额外的设计模式使用。但如果是复杂场景必须考虑设计模式否则代码都写到 SalaryAdjustApplyService 实现类里那么将非常难维护。不要只是把聚合对象当充血模型你的充血结构是整个 domain 下的每一个领域包也就是让这里的状态与行为看做为一整个结构。综上有了这样的模型结构设计定义相信你也可以很好的拆分自己的业务对象并完成领域功能实现了。 三、配置文件工程中关于 MyBatis 的使用在 xfg-dev-tech-app 下进行统一配置。因为所有配置信息都放到一起比较方便管理也利于线上上线后提取配置文件。 四、功能实现 接下来我们介绍一些关于 MyBatis 的使用功能但你可以带着 DDD 的思想来看这些内容实现时所在的位置这会让你不只是学习 MyBatis 也能学会一些 DDD 的设计。 1. 插入批量插入 源码cn.bugstack.xfg.dev.tech.infrastructure.dao.IEmployeeDAO Mapper public interface IEmployeeDAO {void insert(EmployeePO employee);void insertList(ListEmployeePO list);void update(EmployeePO employeePO);EmployeePO queryEmployeeByEmployNumber(String employNumber);}xmlemployee_mapper.xmlinsert idinsert parameterTypecn.bugstack.xfg.dev.tech.infrastructure.po.EmployeePOINSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time)VALUES(#{employeeNumber}, #{employeeName}, #{employeeLevel}, #{employeeTitle}, now(), now()) /insertinsert idinsertList parameterTypejava.util.ListINSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time)VALUESforeach collectionlist itemitem separator,(#{item.employeeNumber}, #{item.employeeName}, #{item.employeeLevel}, #{item.employeeTitle}, now(), now())/foreach /insert使用配置文件的方式比较好维护当然也可以尝试使用 MyBatis 提供的注解方式完成数据的操作。 2. 事务注解编程 Spring 提供的事务分为注解事务和编程事务编程事务可以更细粒度的控制。 Spring Boot 事务管理的级别可以通过 Transactional 注解的 isolation 属性进行配置。常见的事务隔离级别有以下几种 DEFAULT使用底层数据库的默认隔离级别。MySQL 默认为 REPEATABLE READOracle 默认为 READ COMMITTED。READ_UNCOMMITTED最低的隔离级别允许读取未提交的数据变更可能会导致脏读、不可重复读和幻读问题。READ_COMMITTED允许读取已经提交的数据变更可以避免脏读问题但可能会出现不可重复读和幻读问题。REPEATABLE_READ保证同一事务中多次读取同一数据时结果始终一致可以避免脏读和不可重复读问题但可能会出现幻读问题。SERIALIZABLE最高的隔离级别可以避免脏读、不可重复读和幻读问题但会影响并发性能。 在 Spring Boot 中默认的事务隔离级别为 DEFAULT。如果没有特殊需求建议使用默认隔离级别。 SpringBoot 事务的传播行为可以通过 Transactional 注解的 propagation 属性进行配置。常用的传播行为有以下几种 Propagation.REQUIRED默认的传播行为如果当前存在事务则加入该事务否则新建一个事务Propagation.SUPPORTS如果当前存在事务则加入该事务否则以非事务的方式执行Propagation.MANDATORY如果当前存在事务则加入该事务否则抛出异常Propagation.REQUIRES_NEW无论当前是否存在事务都会新建一个事务如果当前存在事务则将当前事务挂起Propagation.NOT_SUPPORTED以非事务的方式执行操作如果当前存在事务则将当前事务挂起Propagation.NEVER以非事务的方式执行操作如果当前存在事务则抛出异常Propagation.NESTED如果当前存在事务则在该事务的嵌套事务中执行否则新建一个事务。嵌套事务是独立于外部事务的但是如果外部事务回滚则嵌套事务也会回滚。 除了传播行为Transactional 注解还可以配置其他属性例如隔离级别、超时时间、只读等。 2.1 注解事务 源码cn.bugstack.xfg.dev.tech.infrastructure.repository.SalaryAdjustRepository Transactional(rollbackFor Exception.class, timeout 350, propagation Propagation.REQUIRED, isolation Isolation.DEFAULT) public String adjustSalary(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate) {String employeeNumber adjustSalaryApplyOrderAggregate.getEmployeeNumber();String orderId adjustSalaryApplyOrderAggregate.getOrderId();EmployeeEntity employeeEntity adjustSalaryApplyOrderAggregate.getEmployeeEntity();EmployeeSalaryAdjustEntity employeeSalaryAdjustEntity adjustSalaryApplyOrderAggregate.getEmployeeSalaryAdjustEntity();EmployeePO employeePO EmployeePO.builder().employeeNumber(employeeNumber).employeeLevel(employeeEntity.getEmployeeLevel().getCode()).employeeTitle(employeeEntity.getEmployeeTitle().getDesc()).build();// 更新岗位employeeDAO.update(employeePO);EmployeeSalaryPO employeeSalaryPO EmployeeSalaryPO.builder().employeeNumber(employeeNumber).salaryTotalAmount(employeeSalaryAdjustEntity.getAdjustTotalAmount()).salaryMeritAmount(employeeSalaryAdjustEntity.getAdjustMeritAmount()).salaryBaseAmount(employeeSalaryAdjustEntity.getAdjustBaseAmount()).build();// 更新薪酬employeeSalaryDAO.update(employeeSalaryPO);EmployeeSalaryAdjustPO employeeSalaryAdjustPO EmployeeSalaryAdjustPO.builder().employeeNumber(employeeNumber).adjustOrderId(orderId).adjustTotalAmount(employeeSalaryAdjustEntity.getAdjustTotalAmount()).adjustBaseAmount(employeeSalaryAdjustEntity.getAdjustMeritAmount()).adjustMeritAmount(employeeSalaryAdjustEntity.getAdjustBaseAmount()).build();// 写入流水employeeSalaryAdjustDAO.insert(employeeSalaryAdjustPO);return orderId; }这个事务所做的内容就是前面提到的调整薪资的处理。它的具体操作就是放到仓储层实现。注意事务注解的配置。 2.2 编程事务 2.2.1 事务模板使用编程事务需要在这里创建出一个事务模板当然你不创建也可以使用。只不过这样统一的配置会更加方便。 2.2.2 事务使用 private TransactionTemplate transactionTemplate; Override public void insertEmployeeInfo(EmployeeInfoEntity employeeInfoEntity) {transactionTemplate.execute(new TransactionCallbackWithoutResult() {Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {try {EmployeePO employeePO EmployeePO.builder().employeeNumber(employeeInfoEntity.getEmployeeNumber()).employeeName(employeeInfoEntity.getEmployeeName()).employeeLevel(employeeInfoEntity.getEmployeeLevel()).employeeTitle(employeeInfoEntity.getEmployeeTitle()).build();employeeDAO.insert(employeePO);EmployeeSalaryPO employeeSalaryPO EmployeeSalaryPO.builder().employeeNumber(employeeInfoEntity.getEmployeeNumber()).salaryTotalAmount(employeeInfoEntity.getSalaryTotalAmount()).salaryMeritAmount(employeeInfoEntity.getSalaryMeritAmount()).salaryBaseAmount(employeeInfoEntity.getSalaryBaseAmount()).build();employeeSalaryDAO.insert(employeeSalaryPO);} catch (Exception e) {status.setRollbackOnly();e.printStackTrace();}}}); } 之后就可以手动处理事务了因为手动的处理可以更细节的控制也可以根据返回的结果手动回滚。而不非得异常回滚。 3. 插件数据加密 使用 MyBatis 时也会经常会用到插件开发。尤其是做一些数据的加解密、路由、日志等都可以基于插件实现。 那么这里实现一个对指定字段加解密的处理比如雇员的姓名、薪资、级别是可以隐藏的避免被有心之人盗取。 源码cn.bugstack.xfg.dev.tech.plugin.FieldEncryptionAndDecryptionMybatisPlugin Intercepts({Signature(type Executor.class, method update, args {MappedStatement.class, Object.class}),Signature(type Executor.class, method query, args {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class FieldEncryptionAndDecryptionMybatisPlugin implements Interceptor {/*** 密钥必须是16位*/private static final String KEY 1898794876567654;/*** 偏移量必须是16位*/private static final String IV 1233214566547891;Overridepublic Object intercept(Invocation invocation) throws Throwable {Object[] args invocation.getArgs();MappedStatement mappedStatement (MappedStatement) args[0];Object parameter args[1];String sqlId mappedStatement.getId();if (parameter ! null (sqlId.contains(insert) || sqlId.contains(update)) ) {String columnName employeeName;if (parameter instanceof Map) {ListObject parameterList (ListObject) ((Map?, ?) parameter).get(list);for (Object obj : parameterList) {if (hasField(obj, columnName)) {String fieldValue BeanUtils.getProperty(obj, columnName);String encryptedValue encrypt(fieldValue);BeanUtils.setProperty(obj, columnName, encryptedValue);}}} else {if (hasField(parameter, columnName)) {String fieldValue BeanUtils.getProperty(parameter, columnName);String encryptedValue encrypt(fieldValue);BeanUtils.setProperty(parameter, columnName, encryptedValue);}}}Object result invocation.proceed();if (result ! null sqlId.contains(query)) {// 查询操作解密String columnName employeeName;if (result instanceof List) {ListObject resultList (ListObject) result;for (Object obj : resultList) {if (!hasField(obj, columnName)) continue;String fieldValue BeanUtils.getProperty(obj, columnName);if (StringUtils.isBlank(fieldValue)) continue;String decryptedValue decrypt(fieldValue);BeanUtils.setProperty(obj, columnName, decryptedValue);}}}return result;}public String encrypt(String content) throws Exception {Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding);byte[] raw KEY.getBytes();SecretKeySpec secretKeySpec new SecretKeySpec(raw, AES);IvParameterSpec ivParameterSpec new IvParameterSpec(IV.getBytes());cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);byte[] encrypted cipher.doFinal(content.getBytes());return Base64.getEncoder().encodeToString(encrypted);}/*** AES解密** param content 密文* return 明文* throws Exception 异常*/public String decrypt(String content) throws Exception {Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding);byte[] raw KEY.getBytes();SecretKeySpec secretKeySpec new SecretKeySpec(raw, AES);IvParameterSpec ivParameterSpec new IvParameterSpec(IV.getBytes());cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);byte[] encrypted Base64.getDecoder().decode(content);byte[] original cipher.doFinal(encrypted);return new String(original);}public boolean hasField(Object obj, String fieldName) {Class? clazz obj.getClass();while (clazz ! null) {try {Field field clazz.getDeclaredField(fieldName);return true;} catch (NoSuchFieldException e) {clazz clazz.getSuperclass();}}return false;}}首先通过注解配置拦截指定范围内的信息 Intercepts 之后在 intercept 接口实现方法中获取 MappedStatement 这个 MyBatis的映射核心类。有了 AES 的加解密就可以对指定的字段 employeeName 对插入数据库的字段进行加密同时还可以在读取的时候解密。 五、测试验证 1. 调薪 Test public void test_execSalaryAdjust() {AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate AdjustSalaryApplyOrderAggregate.builder().employeeNumber(10000001).orderId(100908977676001).employeeEntity(EmployeeEntity.builder().employeeLevel(EmployeePostVO.T3).employeeTitle(EmployeePostVO.T3).build()).employeeSalaryAdjustEntity(EmployeeSalaryAdjustEntity.builder().adjustTotalAmount(new BigDecimal(100)).adjustBaseAmount(new BigDecimal(80)).adjustMeritAmount(new BigDecimal(20)).build()).build();String orderId salaryAdjustApplyService.execSalaryAdjust(adjustSalaryApplyOrderAggregate);log.info(调薪测试 req: {} res: {}, JSON.toJSONString(adjustSalaryApplyOrderAggregate), orderId); }23-07-15.13:23:11.514 [main ] INFO HikariDataSource - HikariPool-1 - Start completed. 23-07-15.13:23:11.910 [main ] INFO ISalaryAdjustApplyServiceTest - 调薪测试 req: {employeeEntity:{employeeLevel:T3,employeeTitle:T3},employeeNumber:10000001,employeeSalaryAdjustEntity:{adjustBaseAmount:80,adjustMeritAmount:20,adjustTotalAmount:100},orderId:100908977676002} res: 1009089776760022. 查询 Test public void test_queryEmployInfo() {EmployeeInfoEntity employeeInfoEntity employeeService.queryEmployInfo(10000001);log.info(测试结果{}, JSON.toJSONString(employeeInfoEntity)); }23-07-15.13:24:54.000 [main ] INFO HikariDataSource - HikariPool-1 - Start completed. 23-07-15.13:24:54.490 [main ] INFO IEmployeeServiceTest - 测试结果{employeeLevel:T-3,employeeName:小哥哥,employeeNumber:10000001,employeeTitle:中级工程师,salaryBaseAmount:5200.00,salaryMeritAmount:5200.00,salaryTotalAmount:5200.00}执行完调薪后就可以来看下这个用户的薪资待遇是多少了。
http://www.pierceye.com/news/950588/

相关文章:

  • 北京网站设计外包公司价格网站怎么备案在哪里
  • 视频网站广告代码网站建设怎么插图片
  • 网站建设需要敲代码吗外贸网站商城
  • wordpress增加网站网页关键词企业网站的需求是什么
  • 口碑好网站建设电话什么是搜索引擎优化用一句话概括
  • wordpress的vps建站流程ui是什么意思
  • 广州专业的网站建设公司哪家好wordpress博客优秀
  • 没有服务器建网站免费开发软件制作平台
  • 高端品牌网站建设明细报价报青岛市公共资源交易网
  • 余姚 网站建设济南网站开发薪酬
  • 一流高职院校建设工作网站野花香社区在线观看播放
  • 大连建设科技网站绿色主题 wordpress
  • 外网访问不了内网建设的网站做网站电销
  • 桂平市住房和城乡建设局网站杭州app定制公司
  • 免费做金融网站自己做网站想更换网址
  • 浙江省网站建设公司成都五月花网页设计培训
  • 江苏外贸网站建设网站开发工作流审批流
  • 自媒体论坛交流推荐网站外部优化
  • 南宁网络建站直接用源码做网站盗版吗
  • 哪些网站可以做驾考试题旅游网站排名排行榜
  • 网站宣传专利被罚账户竞价托管费用
  • 芙蓉区网站建设qq建设网站首页
  • 做音乐网站怎么放音乐常州网红打卡景点
  • 网站做的题不小心关闭了如何成为室内设计师
  • 营销型网站建设公司地址外贸网站seo优化
  • 建设网站是什么科目wordpress对接易支付宝
  • wordpress英文意思能做SEO优化的网站建设
  • 海沧建设网站多少一站式服务英文
  • wordpress网站需要多大空间建设营销型网站
  • id97网站怎么做的项目营销策划方案