二手代码程序员

二手代码程序员 查看完整档案

武汉编辑  |  填写毕业院校  |  填写所在公司/组织 cwsong.top 编辑
编辑

我是萌新,Code splicer,分享一些可能并没什么用的东西,如有异议,欢迎提出来,你说的都对

个人动态

二手代码程序员 回答了问题 · 2020-12-24

解决OpenFeign服务方返回null对象,调用方得到的却是一个具体对象,但是字段为null

找到问题了,是因为我之前引用了spring-cloud-purgeteam导致的,取消引用就好了

关注 1 回答 1

二手代码程序员 提出了问题 · 2020-12-24

Nuxt进行服务器端渲染可以选择传统后台吗?比如Java之类的?

最近想学习Nuxt,看视频都是清一色的使用脚手架然后用脚手架里面的后台选项.想了解下可以是否使用传统后台进行渲染?网上这类资料也比较少.

关注 1 回答 1

二手代码程序员 提出了问题 · 2020-12-15

解决OpenFeign服务方返回null对象,调用方得到的却是一个具体对象,但是字段为null

问题描述

OpenFeign服务方返回null对象,调用方得到的却是一个具体对象,但是字段为null

问题出现的环境背景及自己尝试过哪些方法

在做注册功能的时候通过Auth模块传递参数username去user模块查找,然后返回user对象(无论是否为null),然后对user模块返回内容进行检验,根据是否为空进行后面的逻辑操作,但是当我user模块返回的user对象为null的时候auth模块得到的user对象不为Null,但是他的字段为null导致判空操作不命中.目前是通过获取返回的userId进行判空进行替代解决方案

相关代码

AuthController

  @PostMapping( "/regist/password" )
 public String regist( @RequestBody @Valid UserRegistByPasswordReqVo userRegistByPasswordReqVo ,
 BindingResult result ){
 if( result.hasErrors() ) {
 throw new BusinessException( "-1" , Objects.requireNonNull( result.getFieldError() ).getDefaultMessage() );
 }
 User user = userService.findUser( userRegistByPasswordReqVo.getUsername() );
 //目前的替代方案
 //if( user.getId() != null ) {
 //throw new AuthException( //AuthErrorCode.ACCOUNT_EXIST_USERNAME );
 }
// 通过判断user是否为null的方案
  Optional.ofNullable( user ).ifPresent( u -> {
   if( u.getId() != null ) {throw new AuthException( AuthErrorCode.ACCOUNT_EXIST_USERNAME );}
  } );
 userService.addUser( userRegistByPasswordReqVo );
 return "success";
 }

Auth模块的UserService

@FeignClient( "demos-user" )
public interface UserService{
  
@GetMapping( "/user/{username}" )
 User findUser( @PathVariable String username );
 @PostMapping("/user")
 void addUser( UserRegistByPasswordReqVo userRegistByPasswordReqVo );
}

UserController

@PostMapping
 @IgnoreResponseAdvice(errorDispose = false)
 public User addUser( @RequestBody UserRegistByPasswordReqVo userRegistByPasswordReqVoVo ){
 return userService.addUser( userRegistByPasswordReqVoVo );
//  throw new UserException( UserErrorCode.TEST_ERROR );
 }
 @GetMapping( "/{username}" )
 public User findUser( @PathVariable String username ){
 try {
 Thread.sleep( 2000L );
 } catch( InterruptedException e ){
 e.printStackTrace();
 }
 return userService.findByUsername( username );
 }

你期待的结果是什么?实际看到的错误信息又是什么?

期待的结果:
userService.findUser()返回Null的时候

Optional.ofNullable( user ).ifPresent( u -> {
   if( u.getId() != null ) {throw new AuthException( AuthErrorCode.ACCOUNT_EXIST_USERNAME );}
  } );

不会抛出异常,user为null

实际看到的错误:
image.png

findUser查询出的对象为null,
image.png
通过debug可以看到user为null,没有地址值,而调用方的断点却显示有地址值,字段为null.
image.png
image.png

关注 1 回答 1

二手代码程序员 提出了问题 · 2020-11-29

@ManyToOne如何更新主键

题目描述

使用@ManyToOne如何更新主键

题目来源及自己的思路

用户表和部门表多对一的关系,现在在用户表上使用@ManyToOne,部门表使用了@OneToMany,然后当我想要修改用户的部门时,调用修改接口,传递参数为用户ID和部门ID,请问如何去更新呢?

相关代码

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table (name = "t_dept")
public class Department extends BaseEntity {
 @Column(columnDefinition="varchar(20) DEFAULT '' COMMENT '部门中文名'")
 private String deptName;
 @Column(columnDefinition="varchar(20) DEFAULT '' COMMENT '部门名'")
 private String dept;
 @Column(columnDefinition="int DEFAULT 0 COMMENT '上级部门ID'")
 private Integer parentDeptId;
 @OneToMany (mappedBy = "dept",cascade= CascadeType.ALL,fetch= FetchType.LAZY)
 private Set< User > users;
 @OneToMany (mappedBy = "dept",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
 private Set< Role > roles;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "t_user")
public class User extends BaseEntity  {
 @Column(columnDefinition="varchar(20) DEFAULT '' COMMENT '用户名'",nullable = false)
 private String username;
 @Column(columnDefinition="varchar(100) DEFAULT '' COMMENT '密码'",nullable = false)
 private String password;
 @ManyToOne ( cascade = CascadeType.ALL, fetch = FetchType.LAZY )
 @JoinColumn ( name = "dept_id" )
 private Department dept;
 @ManyToOne ( cascade = CascadeType.ALL, fetch = FetchType.LAZY )
 @JoinColumn ( name = "role_id" )
 private Role role;
  }
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserUpdateReq {
 @NotNull
 private Integer id;
 @Length (max=20,min = 8,message = "用户名长度8-20位")
 private String username;
 @Length(max=16,min = 8,message = "密码长度8-16位")
 private String password;
 @NotNull
 private Integer deptId;
 @NotNull
 private Integer roleId;
}

你期待的结果是什么?实际看到的错误信息又是什么?

我希望查询出来的结果是用户关联部门信息,例如当传的deptId为2的时候,用户由部门1变成部门2的信息,实际错误是使用了CascadeType会导致对应部门ID信息被变更为null,但是不加CascadType的话会导致用户没有部门信息

关注 3 回答 2

二手代码程序员 关注了标签 · 2020-11-29

springboot

Spring Boot 项目旨在简化创建产品级的 Spring 应用和服务。你可通过它来选择不同的 Spring 平台。

关注 14996

二手代码程序员 关注了标签 · 2020-11-29

spring

Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。

关注 28447

二手代码程序员 关注了标签 · 2020-11-29

c++

C++是一种静态数据类型检查的,支持多范型的通用程序设计语言。C++支持过程化程序设计、数据抽象化、面向对象程序设计、泛型程序设计、基于原则设计等多种程序设计风格。

关注 14705

二手代码程序员 赞了文章 · 2020-11-24

讲武德,你们要的高性能日志工具 Log4j2,来了

Log4j 介绍过了,SLF4J 介绍过了,Logback 也介绍过了,你以为日志系列的文章就到此终结了?

不不不,我告诉你,还有一个 Log4j 2,顾名思义,它就是 Log4j 的升级版,就好像手机里面的 Pro 版。我作为一个写文章方面的工具人,或者叫打工人,怎么能不写完这最后一篇。

Log4j、SLF4J、Logback 是一个爹——Ceki Gulcu,但 Log4j 2 却是例外,它是 Apache 基金会的产品。

SLF4J 和 Logback 作为 Log4j 的替代品,在很多方面都做了必要的改进,那为什么还需要 Log4j 2 呢?我只能说 Apache 基金会的开发人员很闲,不,很拼,要不是他们这种精益求精的精神,这个编程的世界该有多枯燥,毕竟少了很多可以用“拿来就用”的轮子啊。

上一篇也说了,老板下死命令要我把日志系统切换到 Logback,我顺利交差了,老板很开心,夸我这个打工人很敬业。为了表达对老板的这份感谢,我决定偷偷摸摸地试水一下 Log4j 2,尽管它还不是个成品,可能会会项目带来一定的隐患。但谁让咱是一个敬岗爱业的打工人呢。

01、Log4j 2 强在哪

1)在多线程场景下,Log4j 2 的吞吐量比 Logback 高出了 10 倍,延迟降低了几个数量级。这话听起来像吹牛,反正是 Log4j 2 官方自己吹的。

Log4j 2 的异步 Logger 使用的是无锁数据结构,而 Logback 和 Log4j 的异步 Logger 使用的是 ArrayBlockingQueue。对于阻塞队列,多线程应用程序在尝试使日志事件入队时通常会遇到锁争用。

下图说明了多线程方案中无锁数据结构对吞吐量的影响。 Log4j 2 随着线程数量的扩展而更好地扩展:具有更多线程的应用程序可以记录更多的日志。其他日志记录库由于存在锁竞争的关系,在记录更多线程时,总吞吐量保持恒定或下降。这意味着使用其他日志记录库,每个单独的线程将能够减少日志记录。

性能方面是 Log4j 2 的最大亮点,至于其他方面的一些优势,比如说下面这些,可以忽略不计,文字有多短就代表它有多不重要。

2)Log4j 2 可以减少垃圾收集器的压力。

3)支持 Lambda 表达式。

4)支持自动重载配置。

02、Log4j 2 使用示例

废话不多说,直接实操开干。理论知识有用,但不如上手实操一把,这也是我多年养成的一个“不那么良好”的编程习惯:在实操中发现问题,解决问题,寻找理论基础。

第一步,在 pom.xml 文件中添加 Log4j 2 的依赖:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.5</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.5</version>
</dependency>

(这个 artifactId 还是 log4j,没有体现出来 2,而在 version 中体现,多少叫人误以为是 log4j)

第二步,来个最简单的测试用例:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Demo {
    private static final Logger logger = LogManager.getLogger(Demo.class);
    public static void main(String[] args) {
        logger.debug("log4j2");
    }
}

运行 Demo 类,可以在控制台看到以下信息:

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.

Log4j 2 竟然没有在控制台打印“ log4j2”,还抱怨我们没有为它指定配置文件。在这一点上,我就觉得它没有 Logback 好,毕竟人家会输出。

这对于新手来说,很不友好,因为新手在遇到这种情况的时候,往往不知所措。日志里面虽然体现了 ERROR,但代码并没有编译出错或者运行出错,凭什么你不输出?

那作为编程老鸟来说,我得告诉你,这时候最好探究一下为什么。怎么做呢?

我们可以复制一下日志信息中的关键字,比如说:“No log4j2 configuration file found”,然后在 Intellij IDEA 中搜一下,如果你下载了源码和文档的话,不除意外,你会在 ConfigurationFactory 类中搜到这段话。

可以在方法中打个断点,然后 debug 一下,你就会看到下图中的内容。

通过源码,你可以看得到,Log4j 2 会去寻找 4 种类型的配置文件,后缀分别是 properties、yaml、json 和 xml。前缀是 log4j2-test 或者 log4j2。

得到这个提示后,就可以进行第三步了。

第三步,在 resource 目录下增加 log4j2-test.xml 文件(方便和 Logback 做对比),内容如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

Log4j 2 的配置文件格式和 Logback 有点相似,基本的结构为 < Configuration> 元素,包含 0 或多个 < Appenders> 元素,其后跟 0 或多个 < Loggers> 元素,里面再跟最多只能存在一个的 < Root> 元素。

1)配置 appender,也就是配置日志的输出目的地。

有 Console,典型的控制台配置信息上面你也看到了,我来简单解释一下里面 pattern 的格式:

  • %d{HH:mm:ss.SSS} 表示输出到毫秒的时间
  • %t 输出当前线程名称
  • %-5level 输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补空格
  • %logger 输出 logger 名称,最多 36 个字符
  • %msg 日志文本
  • %n 换行

顺带补充一下其他常用的占位符:

  • %F 输出所在的类文件名,如 Demo.java
  • %L 输出行号
  • %M 输出所在方法名
  • %l 输出语句所在的行数, 包括类名、方法名、文件名、行数
  • %p 输出日志级别
  • %c 输出包名,如果后面跟有 {length.} 参数,比如说 %c{1.},它将输出报名的第一个字符,如 com.itwanger 的实际报名将只输出 c.i

再次运行 Demo 类,就可以在控制台看到打印的日志信息了:

10:14:04.657 [main] DEBUG com.itwanger.Demo - log4j2

2)配置 Loggers,指定 Root 的日志级别,并且指定具体启用哪一个 Appenders。

3)自动重载配置

Logback 支持自动重载配置,Log4j 2 也支持,那想要启用这个功能也非常简单,只需要在 Configuration 元素上添加 monitorInterval 属性即可。

<Configuration monitorInterval="30">
...
</Configuration>

注意值要设置成非零,上例中的意思是至少 30 秒后检查配置文件中的更改。最小间隔为 5 秒。

03、Async 示例

除了 Console,还有 Async,可以配合文件的方式来异步写入,典型的配置信息如下所示:

<Configuration>
  <Appenders>
    <File name="DebugFile" fileName="debug.log">
      <PatternLayout>
        <Pattern>%d %p %c [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
    <Async name="Async">
      <AppenderRef ref="DebugFile"/>
    </Async>
  </Appenders>
  <Loggers>
    <Root level="debug">
      <AppenderRef ref="Async"/>
    </Root>
  </Loggers>
</Configuration>

对比 Logback 的配置文件来看,Log4j 2 真的复杂了一些,不太好用,就这么直白地说吧!但自己约的,含着泪也得打完啊。把这个 Async 加入到 Appenders:

<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
        <File name="DebugFile" fileName="debug.log">
            <PatternLayout>
                <Pattern>%d %p %c [%t] %m%n</Pattern>
            </PatternLayout>
        </File>
        <Async name="Async">
            <AppenderRef ref="DebugFile"/>
        </Async>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="Async"/>
        </Root>
    </Loggers>
</Configuration>

再次运行 Demo 类,可以在项目根路径下看到一个 debug.log 文件,内容如下所示:

2020-10-30 09:35:49,705 DEBUG com.itwanger.Demo [main] log4j2

04、RollingFile 示例

当然了,Log4j 和 Logback 我们都配置了 RollingFile,Log4j 2 也少不了。RollingFile 会根据 Triggering(触发)策略和 Rollover(过渡)策略来进行日志文件滚动。如果没有配置 Rollover,则使用 DefaultRolloverStrategy 来作为 RollingFile 的默认配置。

触发策略包含有,基于 cron 表达式(源于希腊语,时间的意思,用来配置定期执行任务的时间格式)的 CronTriggeringPolicy;基于文件大小的 SizeBasedTriggeringPolicy;基于时间的 TimeBasedTriggeringPolicy。

过渡策略包含有,默认的过渡策略 DefaultRolloverStrategy,直接写入的 DirectWriteRolloverStrategy。一般情况下,采用默认的过渡策略即可,它已经足够强大。

来看第一个基于 SizeBasedTriggeringPolicy 和 TimeBasedTriggeringPolicy 策略,以及缺省 DefaultRolloverStrategy 策略的配置示例:

<Configuration>
  <Appenders>
    <RollingFile name="RollingFile" fileName="rolling.log"
                 filePattern="rolling-%d{yyyy-MM-dd}-%i.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
      <Policies>
        <SizeBasedTriggeringPolicy size="1 KB"/>
      </Policies>
    </RollingFile>
  </Appenders>
  <Loggers>
    <Root level="debug">
      <AppenderRef ref="RollingFile"/>
    </Root>
  </Loggers>
</Configuration>

为了验证文件的滚动策略,我们调整一下 Demo 类,让它多打印点日志:

for (int i = 1;i < 20; i++) {
    logger.debug("微信搜索「{}」,回复关键字「{}」,有惊喜哦","沉默王二", "java");
}

再次运行 Demo 类,可以看到根目录下多了 3 个日志文件:

结合日志文件名,再来看 RollingFile 的配置,就很容易理解了。

1)fileName 用来指定文件名。

2)filePattern 用来指定文件名的模式,它取决于过渡策略。

由于配置文件中没有显式指定过渡策略,因此 RollingFile 会启用默认的 DefaultRolloverStrategy。

先来看一下 DefaultRolloverStrategy 的属性:

再来看 filePattern 的值 rolling-%d{yyyy-MM-dd}-%i.log,其中 %d{yyyy-MM-dd} 很好理解,就是年月日;其中 %i 是什么意思呢?

第一个日志文件名为 rolling.log(最近的日志放在这个里面),第二个文件名除去日期为 rolling-1.log,第二个文件名除去日期为 rolling-2.log,根据这些信息,你能猜到其中的规律吗?

其实和 DefaultRolloverStrategy 中的 max 属性有关,目前使用的默认值,也就是 7,那就当 rolling-8.log 要生成的时候,删除 rolling-1.log。可以调整 Demo 中的日志输出量来进行验证。

3)SizeBasedTriggeringPolicy,基于日志文件大小的时间策略,大小以字节为单位,后缀可以是 KB,MB 或 GB,例如 20 MB。

再来看一个日志文件压缩的示例,来看配置:

<RollingFile name="RollingFileGZ" fileName="gz/rolling.log"
             filePattern="gz/%d{yyyy-MM-dd-HH}-%i.rolling.gz">
    <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
    </PatternLayout>
    <Policies>
        <SizeBasedTriggeringPolicy size="1 KB"/>
    </Policies>
</RollingFile>
  • fileName 的属性值中包含了一个目录 gz,也就是说日志文件都将放在这个目录下。
  • filePattern 的属性值中增加了一个 gz 的后缀,这就表明日志文件要进行压缩了,还可以是 zip 格式。

运行 Demo 后,可以在 gz 目录下看到以下文件:

到此为止,Log4j 2 的基本使用示例就已经完成了。测试环境搞定,我去问一下老板,要不要在生产环境下使用 Log4j 2。

04、日志手册

到此为止,日志系统的全家桶 Log4j、SLF4J、Logback、Log4j 2 都被我搞定了。也就意味着,一份将近 2 万字的 PDF 诞生了!MD,我也要成为肝帝!

有了这份 PDF,硬气地告诉 Leader 或者老板吧,再也不用担心代码中会乱打印日志了。墙都不扶就服你。

地址我贴一下:

链接:https://pan.baidu.com/s/1dPwsQhT5OMVapE7hGi7vww 密码:fxxy

偷偷地告诉你吧,白嫖的感觉就是舒服,赶紧去下载吧!顺带点个赞,满足一下我的虚荣心,毕竟这是你们赐给我的最强的写作动力。

查看原文

赞 8 收藏 4 评论 1

二手代码程序员 提出了问题 · 2020-10-29

关于SpringDataJPA和MapStruct的使用

我使用SpringDataJpa进行多表关联,其中几个实体类的对应情况如下:

User:
public class User extends BaseEntity  {
 private String username;
 private String password;
 @ManyToOne ( cascade = CascadeType.ALL, fetch = FetchType.LAZY )
 @JoinColumn ( name = "dept_id" )
 private Department depts;
 @ManyToOne ( cascade = CascadeType.ALL, fetch = FetchType.LAZY )
 @JoinColumn ( name = "role_id" )
 private Role role;
}
Role :
public class Role extends BaseEntity {
 private String role;
 private String roleName;
 @OneToMany (mappedBy = "roles",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
 private Set<User> users;
 @ManyToMany (mappedBy = "roles",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
 @JoinTable(name = "t_role_route",joinColumns=@JoinColumn(name="role_id",referencedColumnName = "id"),inverseJoinColumns = @JoinColumn(name = "route_id"))
 @JsonIgnore
 private Set<Route> routes;
 @ManyToOne ( cascade = CascadeType.ALL, fetch = FetchType.LAZY )
 @JoinColumn ( name = "dept_id" )
 private Department depts;
}
Route:
public class Route extends BaseEntity {
 private String route;
 private String routeName;
 @ManyToMany(mappedBy = "routes")
 private Set<Role> roles;
}
Department:
public class Department extends BaseEntity {
 private String deptName;
 private String dept;
 @OneToMany (mappedBy = "depts",cascade= CascadeType.ALL,fetch= FetchType.LAZY)
 private Set<User> users;
 @OneToMany (mappedBy = "depts",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
 private Set<Role> roles;
}

现在我在登录后想返回这么一个对象:
UserVo:

private String username;
private String password;
private List<String> routes;
private List<String> routeNames;
private String deptName;
private String dept;
private Collection<? extends GrantedAuthority> authorities;

其中routesrouteNames是role中routes的属性,deptNamedept是user中dept的属性,现在我想在用户登陆的时候根据username 从数据库查询到user以及关联的信息,并且使用mapStruct进行转换成userVO,请问这个该怎么转化呢?

关注 1 回答 0

二手代码程序员 回答了问题 · 2020-10-11

解决hexo init 卡住

我后卡设置了git代理,发现可以成功下载,推测是git卡住了

关注 1 回答 1

认证与成就

  • 获得 0 次点赞
  • 获得 13 枚徽章 获得 0 枚金徽章, 获得 3 枚银徽章, 获得 10 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-07-04
个人主页被 626 人浏览