Java開發工作中常見問題


開發工具

idea打開多微服務面板(workspace.xml)

<component name="RunDashboard">
    <option name="configurationTypes">
      <set>
        <option value="SpringBootApplicationConfigurationType" />
      </set>
    </option>
    <option name="ruleStates">
      <list>
        <RuleState>
          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
        </RuleState>
        <RuleState>
          <option name="name" value="StatusDashboardGroupingRule" />
        </RuleState>
      </list>
    </option>
  </component>

github提交一個項目

1.github提交一個項目
(1)github添加新倉庫jucdemo2
(2)添加客戶端id_ras.pub秘鑰(C:\Users\Czz\.ssh\id_ras.pub)
(3)添加遠程倉庫:
# 初始化本地倉庫,並提交到本地倉庫
$ cd D:\ideawk\jucdemo
$ git init
$ git add .
$ git commit -m 'first commit'
# 關聯遠程倉庫並推送項目
$ git remote add origin git@github.com:line007/jucdemo2.git
# 第一次推送
$ git push -u origin master
# 非第一次推送
$  git push origin master

2.git checkout遠程分支、標簽
命令:git clone --branch [tags標簽] [git地址] 或者 git clone --b [tags標簽] [git地址]
例如:git clone -b 1.4.1 https://github.com/jumpserver/coco.git

git clone -b v6.3.0 https://github.com/theme-next/hexo-theme-next themes/next6.3

idea-pom項目無法識別

File->Settings->Build,Excecution,Deployment->Build Tools->Maven->Ignored Files

查看是否存在maven pom被勾選,去掉勾選即可。

linux

1.常用命令
-- 查看異常
$ tail -n 400 /usr/local/xx/logs/app-demo/error.log

-- 查看端口
$ netstat -tunlp
$ ps -aux | grep 668

-- 查看前10個最大的文件
$ du -a / | sort -nr | head -n 10

$ nohup java -jar xxl-job-admin.jar & 
$ nohup java -jar xxl-job-executor-sample.jar &

2.nginx常用命令
$ nginx -t
$ ./nginx -s reload

3.redis常用命令
-- 查詢刪除KEY
$ key *
$ get menu_details::1_menu
$ del menu_details::1_menu

-- 清庫
// 刪除當前數據庫中的所有Key
$ flushdb
// 刪除所有數據庫中的key
$ flushall

Spring

spring-auth2.0 自定義異常輸出

參考網址

// 核心原理:定義WebResponseExceptionTranslator一個實現類,並將自定義異常處理類添加到認證服務器配置
@Configuration 
@EnableAuthorizationServer 
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired 
    private WebResponseExceptionTranslator webResponseExceptionTranslator;
    /** 用來配置授權(authorization)以及令牌(token)的訪問端點和令牌服務(token services) */ 
    @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
        ... 
        endpoints.exceptionTranslator(webResponseExceptionTranslator); 
        ... 
    } 
    ...
}

// 1.定義繼承OAuth2Exception的異常類
@JsonSerialize(using = PigAuth2ExceptionSerializer.class)
public class PigAuth2Exception extends OAuth2Exception {
   @Getter
   private String errorCode;
   public PigAuth2Exception(String msg) {
      super(msg);
   }
   public PigAuth2Exception(String msg, String errorCode) {
      super(msg);
      this.errorCode = errorCode;
   }
}
// 2.定義序列化實現類
public class PigAuth2ExceptionSerializer extends StdSerializer<PigAuth2Exception> {
   public PigAuth2ExceptionSerializer() {
      super(PigAuth2Exception.class);
   }
   @Override
   @SneakyThrows
   public void serialize(PigAuth2Exception value, JsonGenerator gen, SerializerProvider provider) {
      gen.writeStartObject();
      gen.writeObjectField("code", CommonConstants.FAIL);
      gen.writeStringField("msg", value.getMessage());
      gen.writeStringField("data", value.getErrorCode());
      gen.writeEndObject();
   }
}
// 3.自定義實現異常轉換類
/**
* @author lengleng
* @date 2019/2/1
* 異常處理,重寫oauth 默認實現
*/
@Slf4j
public class PigWebResponseExceptionTranslator implements WebResponseExceptionTranslator {

   private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
   @Override
   @SneakyThrows
   public ResponseEntity<OAuth2Exception> translate(Exception e) {
      // Try to extract a SpringSecurityException from the stacktrace
      Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
      Exception ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class,
         causeChain);
      if (ase != null) {
         return handleOAuth2Exception(new UnauthorizedException(e.getMessage(), e));
      }
      ase = (AccessDeniedException) throwableAnalyzer
         .getFirstThrowableOfType(AccessDeniedException.class, causeChain);
      if (ase != null) {
         return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase));
      }
      ase = (InvalidGrantException) throwableAnalyzer
         .getFirstThrowableOfType(InvalidGrantException.class, causeChain);
      if (ase != null) {
         return handleOAuth2Exception(new InvalidException(ase.getMessage(), ase));
      }
      ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer
         .getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
      if (ase != null) {
         return handleOAuth2Exception(new MethodNotAllowed(ase.getMessage(), ase));
      }
      ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(
         OAuth2Exception.class, causeChain);
      if (ase != null) {
         return handleOAuth2Exception((OAuth2Exception) ase);
      }
      return handleOAuth2Exception(new ServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), e));
   }
   private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) {
      int status = e.getHttpErrorCode();
      HttpHeaders headers = new HttpHeaders();
      headers.set(HttpHeaders.CACHE_CONTROL, "no-store");
      headers.set(HttpHeaders.PRAGMA, "no-cache");
      if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
         headers.set(HttpHeaders.WWW_AUTHENTICATE, String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
      }
      // 客戶端異常直接返回客戶端,不然無法解析
      if (e instanceof ClientAuthenticationException) {
         return new ResponseEntity<>(e, headers,
            HttpStatus.valueOf(status));
      }
      return new ResponseEntity<>(new PigAuth2Exception(e.getMessage(), e.getOAuth2ErrorCode()), headers,
         HttpStatus.valueOf(status));
   }
}
4.將自定義異常處理類添加到認證服務器配置
View Code

SpringBoot添加定時任務

// springboot啟動添加注解@EnableScheduling
@EnableScheduling
public class DemoApplication {
   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}
// task
@Slf4j
@Component
public class DemoTask {
   @Autowired
   private XxService xxService;

   @Scheduled(cron="0 30 2 * * ? ")
   protected void process(){
      log.info("EbOrderSyncTask同步開始 =========>" + LocalDateTime.now());
      ...
      log.info("EbOrderSyncTask同步結束 =========>" + LocalDateTime.now());
   }
}
View Code

springboot+redis+AOP分布式

參考網址

spring-cloud引入swagger

@Component
@Primary
public class DocumentationConfig implements SwaggerResourcesProvider {
   @Override
   public List<SwaggerResource> get() {
      List resources = new ArrayList<>();
      // service-id => xx-appName
      // swagger url => "/admin/v2/api-docs"
      resources.add(swaggerResource("xx-appName", "/admin/v2/api-docs", "2.0"));
      return resources;
   }
   private SwaggerResource swaggerResource(String name, String location, String version) {
      SwaggerResource swaggerResource = new SwaggerResource();
      swaggerResource.setName(name);
      swaggerResource.setLocation(location);
      swaggerResource.setSwaggerVersion(version);
      return swaggerResource;
   }
}

@Configuration
@EnableSwagger2
public class SystemSwaggerConfig extends WebMvcConfigurationSupport {
    @Bean
    public Docket api() {
      return new Docket(DocumentationType.SWAGGER_2)
         .host("128.0.0.1:27000")
         .apiInfo(apiInfo())
         .select()
         // 自行修改為自己的包路徑 -> com.xx.controller
         .apis(RequestHandlerSelectors.basePackage("com.xx.controller"))
         .paths(PathSelectors.any())
         .build()
         .globalOperationParameters(getParams());
    }
   private List<Parameter> getParams() {
      //添加head參數start
      ParameterBuilder tokenPar = new ParameterBuilder();
      List<Parameter> pars = new ArrayList<Parameter>();
      tokenPar.name("Authorization").description("令牌").modelRef(new ModelRef("string"))
         .parameterType("header").required(false)
         .defaultValue("Bearer 15265af2-607d-4c8b-ad54-0c434af82849")
         .build();
      pars.add(tokenPar.build());
      return pars;
   }
   @Override
   protected void addResourceHandlers(ResourceHandlerRegistry registry) {
      registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");;
      registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
      super.addResourceHandlers(registry);
   }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("接口文檔").build();
    }
}
View Code

spring-MockMvc請求方式

// 1.路徑請求
mockMvc.perform(MockMvcRequestBuilders.請求方式("url/{path}",參數值);
                
// 2.表單請求
mockMvc.perform(MockMvcRequestBuilders.請求方式("url").param("","").contentType(MediaType.APPLICATION_FORM_URLENCODED);                
// 3.JSON請求
mockMvc.perform(MockMvcRequestBuilders.請求方式,一般為POST("url").content(JSONObject.toJSONString(map)).contentType(.contentType(MediaType.APPLICATION_JSON)); 

spring-enable-xx注解原理

[參考網址1]
[參考網址2]

所有@Enable* 注解都是有@Import的組合注解,@Enable* 自動開啟的實現其實就是導入了一些自動配置的Bean,@Import 注解的最主要功能就是導入額外的配置信息。

@Import 注解的用法
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}

/**
 * 可以看到EnableScheduling注解直接導入配置類SchedulingConfiguration,這個類注解了@Configuration,且注冊了一個scheduledAnnotationProcessor的Bean
 */
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}
View Code
實現 ImportSelector 接口
/**
如果並不確定引入哪個配置類,需要根據@Import注解所標識的類或者另一個注解(通常是注解)里的定義信息選擇配置類的話,用這種方式。*/
// ImportSelector接口只有一個方法:
String[] selectImports(AnnotationMetadata importingClassMetadata);

/** 注解類 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
    Class<? extends Annotation> annotation() default Annotation.class;
    boolean proxyTargetClass() default false;
    AdviceMode mode() default AdviceMode.PROXY;
    int order() default Ordered.LOWEST_PRECEDENCE;
}

/**
 * 實現選擇器
 * AsyncConfigurationSelector繼承AdviceModeImportSelector,AdviceModeImportSelector類實現ImportSelector接口 根據AdviceMode的不同來選擇生明不同的Bean
 */
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    @Override
    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {ProxyAsyncConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }
}
View Code
實現 ImportBeanDefinitionRegistrar接口
/**
一般只要用戶確切知道哪些Bean需要放入容器的話,自己可以通過spring 提供的注解來標識就可以了,比如@Component,@Service,@Repository,@Bean等。 如果是不確定的類,或者不是spring專用的,所以並不想用spring的注解進行侵入式標識,那么就可以通過@Import注解,實現ImportBeanDefinitionRegistrar接口來動態注冊Bean。 比如:
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    boolean proxyTargetClass() default false;
    boolean exposeProxy() default false;
}

/**
 * AspectJAutoProxyRegistrar實現了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar的作用是在運行時自動添加Bean到已有的配置類,通過重寫方法:
 */
AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(
        AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata,         EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}
View Code

springboot自定義日志格式

<!-- resource/logback-spring.xml -->
<configuration debug="false" scan="false">
    <springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
    <property name="log.path" value="logs/${spring.application.name}"/>
    <!-- Console log output -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
       <encoder>
          <pattern>${CONSOLE_LOG_PATTERN}</pattern>
       </encoder>
    </appender>

    <!-- Log file debug output -->
    <appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
       <file>${log.path}/debug.log</file>
       <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
          <fileNamePattern>${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
          <maxFileSize>50MB</maxFileSize>
          <maxHistory>30</maxHistory>
       </rollingPolicy>
       <encoder>
          <pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
       </encoder>
       <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
           <level>DEBUG</level>
       </filter>
    </appender>

    <!-- Log file error output -->
    <appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
           <fileNamePattern>${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
           <maxFileSize>50MB</maxFileSize>
           <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
           <pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    <appender>

    <!-- Level: FATAL 0  ERROR 3  WARN 4  INFO 6  DEBUG 7 -->
    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="debug"/>
        <appender-ref ref="error"/>
    </root>
</configuration>
View Code

引入springboot-task

1.啟用task配置
(1) 配置文件的方式:application.properties
spring.task.scheduling.pool.size=20
spring.task.scheduling.thread-name-prefix=Job-Thread-

(2) 配置類的方式
@Configuration
@EnableScheduling
@ComponentScan(basePackages = {"com.xkcoding.task.job"})
public class TaskConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }
    @Bean
    public Executor taskExecutor() {
        return new ScheduledThreadPoolExecutor(20, new BasicThreadFactory.Builder().namingPattern("Job-Thread-%d").build());
    }
}

2.新增TaskJob類
@Component
@Slf4j
public class TaskJob {
    /**     
     * 按照標准時間來算,每隔 10s 執行一次     
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void job1() {
        log.info("【job1】開始執行:{}", DateUtil.formatDateTime(new Date()));
    }
    /**     
     * 從啟動時間開始,間隔 2s 執行     
     * 固定間隔時間     
     */
    @Scheduled(fixedRate = 2000)
    public void job2() {
        log.info("【job2】開始執行:{}", DateUtil.formatDateTime(new Date()));
    }
    /**     
     * 從啟動時間開始,延遲 5s 后間隔 4s 執行     
     * 固定等待時間     
     */
    @Scheduled(fixedDelay = 4000, initialDelay = 5000)
    public void job3() {
        log.info("【job3】開始執行:{}", DateUtil.formatDateTime(new Date()));
    }
}
View Code

spring類的生命周期

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.2.xsd">       
     <bean id="person1" destroy-method="myDestroy" 
            init-method="myInit" class="com.test.spring.life.Person">
        <property name="name">
            <value>jack</value>
        </property>
    </bean>
    <!-- 配置自定義的后置處理器 -->
     <bean id="postProcessor" class="com.pingan.spring.life.MyBeanPostProcessor" />
</beans>


/**
 * 1.實例化Bean對象
 * 2.設置對象屬性
 * 3.調用BeanNameAware.setBeanName()
 * 4.調用BeanNameAware.setBeanFactory()
 * 5.調用ApplicationContextAware.setApplicationContext()
 * 6.調用BeanPostProcessor.postProcessBeforeInitialzation() -> 前置處理器
 * 7.調用InitializingBean.afterPropertiesSet()
 * 8.調用init-method方法
 * 9.調用BeanPostProcessor.postProcessAfterInitialization() -> 后置處理器
 * 10.緩存一份Bean實例 -> scope="singleton"
 * 11.容器關閉,調用DisposableBean.destroy()
 * 12.調用自定義的destroy-method
 */
public class Person implements BeanNameAware, BeanFactoryAware,
        ApplicationContextAware, InitializingBean, DisposableBean {
    private String name;
    public Person() {
        System.out.println("PersonService類構造方法");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        System.out.println("set方法被調用");
    }
    //自定義的初始化函數
    public void myInit() {
        System.out.println("myInit被調用");
    }
    //自定義的銷毀方法
    public void myDestroy() {
        System.out.println("myDestroy被調用");
    }
    public void destroy() throws Exception {
        // TODO Auto-generated method stub
     System.out.println("destory被調用");
    }
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        System.out.println("afterPropertiesSet被調用");
    }
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        // TODO Auto-generated method stub
       System.out.println("setApplicationContext被調用");
    }
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // TODO Auto-generated method stub
         System.out.println("setBeanFactory被調用,beanFactory");
    }
    public void setBeanName(String beanName) {
        // TODO Auto-generated method stub
        System.out.println("setBeanName被調用,beanName:" + beanName);
    }
    public String toString() {
        return "name is :" + name;
    }
}       
/** Bean前后置處理器 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean,
            String beanName) throws BeansException {
        // TODO Auto-generated method stub
        
        System.out.println("postProcessBeforeInitialization被調用");
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean,
            String beanName) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("postProcessAfterInitialization被調用");
        return bean;
    }
}
/** 測試類 */
public class AcPersonServiceTest {
    public static void main(String[] args) {
        System.out.println("開始初始化容器");
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/test/spring/life/applicationContext.xml"); 
        System.out.println("xml加載完畢");
        Person person1 = (Person) ac.getBean("person1");
        System.out.println(person1);        
        System.out.println("關閉容器");
        ((ClassPathXmlApplicationContext)ac).close();
    }
}

/**
開始初始化容器
九月 25, 2016 10:44:50 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy
九月 25, 2016 10:44:50 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/test/spring/life/applicationContext.xml]
Person類構造方法
set方法被調用
setBeanName被調用,beanName:person1
setBeanFactory被調用,beanFactory
setApplicationContext被調用
postProcessBeforeInitialization被調用
afterPropertiesSet被調用
myInit被調用
postProcessAfterInitialization被調用
xml加載完畢
name is :jack
關閉容器
九月 25, 2016 10:44:51 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@b4aa453: startup date [Sun Sep 25 22:44:50 CST 2016]; root of context hierarchy
destory被調用
myDestroy被調用
*/
View Code

SpringBoot自定義配置文件

@Data
@ConfigurationProperties("example.service")
@Component
public class WrapServiceProperties {
    private String prefix;
    private String suffix;
}

<!-- application.properties -->
example.service.prefix=###
example.service.suffix=@@@
View Code

Springboot-引入xxl-job

1.部署xx-job-admin服務端
(1) 下載項目
$ git clone https://github.com/xuxueli/xxl-job
(2) 初始化doc/tables-xxl_job.sql腳本
(3) 啟動服務,並驗證:
http://127.0.0.1:8080/xxl-job-admin/
初始賬號密碼:admin/123456

2.引入客戶端
(1) 引入jar包(2.2.0版本+服務端2.2.3-SNAPSHOT版本)
appname:在xxl-job配置的執行器的appname
accessToken:調度中心通訊TOKEN,與xxl-job-admin配置文件的token一致即可
<!-- pom.xml -->
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>2.2.0</version>
</dependency>

(2) 配置xxl-job服務器
<!-- application.yml -->
xxl:
  job:
    admin:
      addresses: http://127.0.0.1:8000/xxl-job-admin
    executor:
      appname: xxl-job-client
      ip:
      port: 9999
      logpath: /logs/xxl-job/jobhandler
      logretentiondays: 30
    accessToken: 6889b70769ffb1e955ba69396f0cca6b


(3) 將XxlJobConfig交給spring管理
@Configuration
public class XxlJobConfig {
    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;
    @Value("${xxl.job.executor.appname}")
    private String appName;
    @Value("${xxl.job.executor.ip}")
    private String ip;
    @Value("${xxl.job.executor.port}")
    private int port;
    @Value("${xxl.job.accessToken}")
    private String accessToken;
    @Value("${xxl.job.executor.logpath}")
    private String logPath;
    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;

    // 2.2.0+版本移除
    //@Bean(initMethod = "start", destroyMethod = "destroy")
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
//        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppName(appName);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
        return xxlJobSpringExecutor;
    }
}


(4) 寫一個定時任務
/**
 * XxlJob開發示例(Bean模式)
 *
 * 開發步驟:
 * 1、在Spring Bean實例中,開發Job方法,方式格式要求為 "public ReturnT<String> execute(String param)"
 * 2、為Job方法添加注解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷毀方法")",注解value值對應的是調度中心新建任務的JobHandler屬性的值。
 * 3、執行日志:需要通過 "XxlJobLogger.log" 打印執行日志;
 *
 * @author xuxueli 2019-12-11 21:52:51
 */
@Component
@Sl4j
public class SampleXxlJob {
    /**
     * 1、簡單任務示例(Bean模式)
     */
    @XxlJob("demoJobHandler")
    public ReturnT<String> demoJobHandler(String param) throws Exception {
        log.info("XXL-JOB, Hello World.");

        for (int i = 0; i < 5; i++) {
         XxlJobLogger.log("beat at:" + i);
            TimeUnit.SECONDS.sleep(2);
     }
        return ReturnT.SUCCESS;
    }
}    

(5) 在服務端添加剛剛創建的定時任務
界面輸入參數:
JobHandler:demoJobHandler
View Code

Springboot-引入ActiveMQ

1.引入ActiveMQ支持
(1) pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-activemq</artifactId>
    </dependency>
</dependencies>
(2) application.properties
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin

2.寫demo
(1) 消費者-Consumer
@Component
@EnableJms
@Slf4j
public class ActiveMQListener {
    @JmsListener(destination = "test-queue")
    public void listener(String message) {
        log.info("Message received {} ", message);
    }
}
(2) 生產者-Producer
<!-- JmsConfig.java -->
package com.test.activemq.configuration;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import javax.jms.Queue;
@Configuration
public class JmsConfig {
    @Bean
    public Queue queue(){
        return new ActiveMQQueue("test-queue");
    }
}
<!-- Producer.java -->
package com.test.activemq.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.jms.Queue;

@RestController
@RequestMapping("/api")
public class MessageController {
    @Autowired
    private Queue queue;
    @Autowired
    private JmsTemplate jmsTemplate;
    @GetMapping("message/{message}")
    public ResponseEntity<String> publish(@PathVariable("message") final String message){
        jmsTemplate.convertAndSend(queue, message);
        return new ResponseEntity(message, HttpStatus.OK);
    }
}
View Code

springboot引入動態quartz

package com.line.demo.task;
import java.util.Arrays;
import java.util.List;
/**
 * @author: cent
 * @email: 292462859@qq.com
 * @date: 2019/1/16.
 * @description:
 */
@Configuration
@EnableScheduling
@Slf4j
public class DynamicSchedule implements SchedulingConfigurer {    
    /**
     * 測試數據,實際可從數據庫獲取
     */
    private List<Task> tasks = Arrays.asList(            
        new Task(1, "任務1", "*/30 * * * * *"),            
        new Task(2, "任務2", "*/30 * * * * *"),            
        new Task(3, "任務3", "*/30 * * * * *"),            
        new Task(4, "任務4", "*/30 * * * * *"),            
        new Task(5, "任務5", "*/30 * * * * *"),            
        new Task(6, "任務6", "*/30 * * * * *"),            
        new Task(7, "任務7", "*/30 * * * * *"),            
        new Task(8, "任務8", "*/30 * * * * *"),            
        new Task(9, "任務9", "*/30 * * * * *"),            
        new Task(10, "任務10", "*/30 * * * * *")
    );
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        tasks.forEach(task -> {            
            //任務執行線程
            Runnable runnable = () -> log.info("execute task {}", task.getId());            
            //任務觸發器
            Trigger trigger = triggerContext -> {                
                //獲取定時觸發器,這里可以每次從數據庫獲取最新記錄,更新觸發器,實現定時間隔的動態調整
                CronTrigger cronTrigger = new CronTrigger(task.getCron());                
                return cronTrigger.nextExecutionTime(triggerContext);
            };           
            //注冊任務
            scheduledTaskRegistrar.addTriggerTask(runnable, trigger);
        });
    }    
    
    @Data
    @AllArgsConstructor
    static class Task {        /**
         * 主鍵ID
         */
        private int id;        /**
         * 任務名稱
         */
        private String name;        /**
         * cron表達式
         */
        private String cron;
    }
}
View Code

springboot配置全局異常

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 全局異常.
     *
     * @param e the e
     * @return R
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R exception(Exception e) {
        log.error("全局異常信息 ex={}", e.getMessage(), e);
        return R.failed(e);
    }

    /**
     * validation Exception
     *
     * @param exception
     * @return R
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R bodyValidExceptionHandler(MethodArgumentNotValidException exception) {
        List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
        log.warn(fieldErrors.get(0).getDefaultMessage());
        return R.failed(fieldErrors.get(0).getDefaultMessage());
    }

    /**
     * 處理所有業務異常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    public R handleBusinessException(BusinessException e) {
        log.error("業務異常異常信息 ex={}", e.getMessage(), e);
        return R.failed(e);
    }
}

@NoArgsConstructor
public class BusinessException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public BusinessException(String message) {
        super(message);
    }
    public BusinessException(Throwable cause) {
        super(cause);
    }
    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
    public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

/**
 * 響應信息主體
 *
 * @param <T>
 * @author xw
 */
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    @Getter
    @Setter
    private int code;
    @Getter
    @Setter
    private String msg;
    @Getter
    @Setter
    private T data;

    public static <T> R<T> ok() {
        return restResult(null, CommonConstants.SUCCESS, null);
    }
    public static <T> R<T> ok(T data) {
        return restResult(data, CommonConstants.SUCCESS, null);
    }
    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, CommonConstants.SUCCESS, msg);
    }
    public static <T> R<T> failed() {
        return restResult(null, CommonConstants.FAIL, null);
    }
    public static <T> R<T> failed(String msg) {
        return restResult(null, CommonConstants.FAIL, msg);
    }
    public static <T> R<T> failed(T data) {
        return restResult(data, CommonConstants.FAIL, null);
    }
    public static <T> R<T> failed(T data, String msg) {
        return restResult(data, CommonConstants.FAIL, msg);
    }
    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }
}
View Code

spring獲取某個類的所有子類

String[] beanNamesForType = SpringContextHolder.getApplicationContext().getBeanNamesForType(AbstractObserver.class);

spring-feign傳多個參數

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/get", method = RequestMethod.GET)
  public User get1(@RequestParam("id") Long id, @RequestParam("username") String username);
}

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/get", method = RequestMethod.GET)
  public User get2(@RequestParam Map<String, Object> map);
}

@RestController
public class UserController {
  @PostMapping("/post")
  public User post(@RequestBody User user) {
    ...
  }
}

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/post", method = RequestMethod.POST)
  public User post(@RequestBody User user);
}
View Code

Mysql

初始化數據庫、用戶

使用root賬號創建數據庫db_name並授權給test賬號
$ mysql -uroot -h0 -P3306 -p123456
$ create database db_name;
$ grant all privileges on db_name.* to test@'localhost' identified by '123456';
$ grant all privileges on db_name.* to test@'127.0.0.1' identified by '123456';

導出數據庫和表

-- 導出db_name整個數據庫
mysqldump -u test -p123456 db_name -P 3306 > db_name.sql

-- 導出某數據庫指定的表(db_name.table_name)
mysqldump -u test -p123456 db_name table_name > table_name.sql

導入sql腳本

$ mysql -u test -p123456 db_name < /opt/sql/xx.sql

添加索引

-- 添加PRIMARY KEY(主鍵索引)
ALTER TABLE `table_name` ADD PRIMARY KEY index_name( `column` )
-- 添加UNIQUE(唯一索引)
ALTER TABLE `table_name` ADD UNIQUE index_name( `column` ) 
-- 添加INDEX(普通索引)
ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
-- 添加FULLTEXT(全文索引)
ALTER TABLE `table_name` ADD FULLTEXT ( `column`)  
-- 添加多列索引
ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )

-- 刪除索引
drop index index_name on table_name ;
View Code

修改表字段

-- 添加表的字段 alter table 表名  add  字段名  字段的類型
alter table table1 add transactor varchar(10) not Null;
-- 修改表的字段類型   ALTER TABLE 表名 MODIFY COLUMN 字段名 字段類型定義;
ALTER TABLE chatter_users MODIFY COLUMN ip VARCHAR(50);
-- 修改表的字段名    alter table 表名 change 原字段名  新字段名  字段的類型
alter table student change physics physisc char(10) not null;

-- 刪除表的字段    alter table 表名 drop column 字段名
alter table `user_movement_log` drop column Gatewayid;
-- 調整表的順序:
ALTER TABLE `user_movement_log` CHANGE `GatewayId` `GatewayId` int not null default 0 AFTER RegionID
-- 表的重命名   alter table 原表名 rename 現表名;
alter table t1 rename t2;
-- 刪除表的數據   
delete from 表名  where  (條件)    id 不是從1開始;
truncate table 表名     id是從1 開始的;
-- 創建表的例子  
CREATE TABLE hlh_message (
    id int(11) NOT NULL AUTO_INCREMENT COMMENT '健康表id',
    title varchar(40) NOT NULL COMMENT '健康標題',
    hlh_url text DEFAULT NULL COMMENT '圖片地址',
    bewrite VARCHAR(350) NOT NULL COMMENT '描述',
    content VARCHAR(350) NOT NULL COMMENT '內容',
    type tinyint(1) NOT NULL DEFAULT '0' COMMENT '健康知識 0 健康咨詢 1',
    create_time date DEFAULT NULL COMMENT '發布消息的時間',
    PRIMARY KEY (id)
)ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='健康表';
View Code

Mysql日期操作

-- mysql日期格式化
DATE_FORMAT( created_date, "%Y-%m-%d")

常用查詢

-- mysql查詢奇偶行
set @row = 0;
select id from (select (@row:=case when @row is null then 1 else @row+1 end) as RNr, a.id,a.order_no,a.sku,a.send_count,b.count from cg_supplier_shipping_order_detail a 
inner join cg_buyer_purchase_order b on a.buyer_purchase_order_order_no=b.order_no and a.sku=b.sku where b.order_no='PO119092540003' and b.send_total>b.count) t where t.RNr%2 = 0; -- 查看慢sql show variables like '%query%';

查看mysql表結構和表創建語句

-- 查看所有表
show tables;
-- 查看表結構
desc sys_log;
-- 查看建表語句
show create table tablename;

mysql慢sql查詢

-- show processlist;
-- show variables like '%tmp%';
-- show variables like '%query%';

Mybatis

Mybatis-數據權限

// 使用方法
public interface XxMapper extends BaseMapper<Xx> {
    // dataScope--數據權限載體
    IPage<Xx> getXxPage(Page page, @Param("xx") Xx xx,@Param("dataScope") DataScope dataScope);
}
@Data
@EqualsAndHashCode(callSuper = true)
public class DataScope extends HashMap {
    /**
     * 限制范圍的字段名稱
     */
    private String firstScopeName = "warehouse_id";
    /**
     * 具體的數據范圍
     */
    private List<Integer> firstIds;
    /**
     * 是否只查詢本部門
     */
    private Boolean isOnly = false;
    ...
}

/**
 * @author admin
 * @date 2019/2/1
 */
@Configuration
@MapperScan("com.demo.mapper")
public class MybatisPlusConfigurer {
    /**
     * 分頁插件
     *
     * @return PaginationInterceptor
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor().setLimit(10000);
    }
    /**
     * 數據權限插件
     *
     * @return DataScopeInterceptor
     */
    @Bean
    public DataScopeInterceptor dataScopeInterceptor() {
        return new DataScopeInterceptor();
    }
    /**
     * 邏輯刪除
     *
     * @return
     */
    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }
}

/**
 * @author xw
 * @date 2019/7/30
 * <p>
 * mybatis 數據權限攔截器
 */
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor {

    @Override
    @SneakyThrows
    public Object intercept(Invocation invocation) {
        StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        // 先判斷是不是SELECT操作
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }

        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        String originalSql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();

        //查找參數中包含DataScope類型的參數
        DataScope dataScope = findDataScopeObject(parameterObject);
        if (dataScope == null) {
            return invocation.proceed();
        } else {
            // 數據權限規則 倉庫id+平台id
            StringBuilder sb = new StringBuilder(" where 1=1 ");
            // 第一個范圍
            String firstScopeName = dataScope.getFirstScopeName();
            List<Integer> firstIds = dataScope.getFirstIds();
            if (StrUtil.isNotBlank(firstScopeName) && CollectionUtil.isNotEmpty(firstIds) && firstIds.size() >= 1) {
                sb.append(String.format(" and temp_data_scope.%s in (%s)", firstScopeName, CollectionUtil.join(firstIds, ",")));
            }
            // 第二個范圍(平台權限,當數據平台id為null時,默認所有人可見)
            String secondScopeName = dataScope.getSecondScopeName();
            List<Integer> secondIds = dataScope.getSecondIds();
            if (StrUtil.isNotBlank(secondScopeName) && CollectionUtil.isNotEmpty(secondIds) && secondIds.size() >= 1) {
                sb.append(String.format(" and (temp_data_scope.%s in (%s) or temp_data_scope.%s is null)", secondScopeName, CollectionUtil.join(secondIds, ","), secondScopeName));
            }
            originalSql = "select * from (" + originalSql + ") temp_data_scope" + sb.toString();
            metaObject.setValue("delegate.boundSql.sql", originalSql);
            return invocation.proceed();
        }
    }
    /**
     * 生成攔截對象的代理
     *
     * @param target 目標對象
     * @return 代理對象
     */
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }
    /**
     * mybatis配置的屬性
     *
     * @param properties mybatis配置的屬性
     */
    @Override
    public void setProperties(Properties properties) {

    }
    /**
     * 查找參數是否包括DataScope對象
     *
     * @param parameterObj 參數列表
     * @return DataScope
     */
    private DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                // { param0:xx, param1:xx, xxBean:xx: ...}
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }
}
View Code

MybatisPlus-公共類

/**
 * BaseEntity
 *
 * @author xw
 * @date 2020/5/6
 */
public class BaseEntity<T extends Model<?>> extends Model<T> {
    @ApiModelProperty("創建時間")
    private LocalDateTime createTime;
    @ApiModelProperty("創建人")
    private Integer createUser;
    @ApiModelProperty("更新時間")
    private LocalDateTime updateTime;
    @ApiModelProperty("更新人")
    private Integer updateUser;
    @ApiModelProperty(value = "刪除標識:1-刪除,0-正常,默認為0")
    private Integer delFlag;
    
    // setter、getter
}

/**
 * BaseServiceImpl
 *
 * @author xw
 * @date 2020/5/6
 */
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> {
    @Override
    public boolean save(T entity) {
        entity.setCreateUser(UserUtils.getCurrentUserId());
        entity.setCreateTime(LocalDateTime.now());
        return super.save(entity);
    }
    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        if (CollectionUtil.isNotEmpty(entityList)) {
            entityList.stream().forEach(m -> {
                m.setCreateUser(UserUtils.getCurrentUserId());
                m.setCreateTime(LocalDateTime.now());
            });
        }
        return super.saveBatch(entityList, batchSize);
    }
    @Override
    public boolean updateById(T entity) {
        entity.setUpdateUser(UserUtils.getCurrentUserId());
        entity.setUpdateTime(LocalDateTime.now());
        return super.updateById(entity);
    }
    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        if (CollectionUtil.isNotEmpty(entityList)) {
            entityList.stream().forEach(m -> {
                m.setUpdateUser(UserUtils.getCurrentUserId());
                m.setUpdateTime(LocalDateTime.now());
            });
        }
        return super.updateBatchById(entityList, batchSize);
    }
}
View Code

MybatisPlus常用注解

/**example
 * 刪除時 update user set deleted=1 where id =1 and deleted=0
 * 查找時 select * from user where deleted=0
 */
@TableLogic
private Integer deleted;

// 忽略實體bean非數據庫字段
@TableField(exist=false) 

// 新增、修改值為null字段
@TableField(fill = FieldFill.INSERT_UPDATE)
View Code

MybatisPlus lamba表達式使用

$condition = Wrappers.<~>query().lambda().eq(SysUserRole::getRoleId, 5);
xxService.list($condition)
xxService.getOne($condition)
...
View Code

MybatisPlus分頁

public Page<T> selectPage(Page<T> page, EntityWrapper<T> entityWrapper) { 
    if (null != entityWrapper) { 
        entityWrapper.orderBy(page.getOrderByField(), page.isAsc()); 
    } 
    page.setRecords(baseMapper.selectPage(page, entityWrapper)); 
    return page; 
}
View Code

MybatisPlus拼原生

EntityWrapper<User> ew = new EntityWrapper<User>();
ew.setEntity(new User(1)); 
ew.where("user_name={0}", "'zhangsan'").and("id=1") 
    .orNew("user_status={0}", "0").or("status=1") 
    .notLike("user_nickname", "notvalue") 
    .andNew("new=xx").like("hhh", "ddd") 
    .andNew("pwd=11").isNotNull("n1,n2").isNull("n3") 
    .groupBy("x1").groupBy("x2,x3") 
    .having("x1=11").having("x3=433") 
    .orderBy("dd").orderBy("d1,d2"); 
System.out.println(ew.getSqlSegment());
View Code

MybatisPlus自定義sql

List<User> selectMyPage(RowBounds rowBounds, @Param("ew") Wrapper<T> wrapper);
<select id="selectMyPage" resultType="User"> 
    SELECT * FROM user 
    <where> 
        ${ew.sqlSegment} 
    </where> 
</select>
View Code

Mybatis-like

<if test="xx.name != null and xx.name != ''">
    AND name like CONCAT('%',#{xx.name},'%')
</if>
View Code

MybatisPlus-update

// 方式一
CkQcOrderInfo qcOrderInfo = this.getById(qcOrderDetail.getQcOrderId());
this.update(Wrappers.<CkQcOrderInfo>lambdaUpdate()
            .eq(CkQcOrderInfo::getId, qcOrderDetail.getQcOrderId())
            .set(CkQcOrderInfo::getQcQty, NumUtils.add(qcOrderInfo.getQcQty(), qcOrderDetail.getQcQty()))
            .set(CkQcOrderInfo::getPassQty, NumUtils.add(qcOrderInfo.getPassQty(), qcOrderDetail.getPassQty())));

// 方式二
CkQcOrderInfo qcOrderInfo = this.getById(qcOrderDetail.getQcOrderId());
qcOrderInfo.setQcQty(NumUtils.add(qcOrderInfo.getQcQty(), qcOrderDetail.getQcQty()));
qcOrderInfo.setPassQty(NumUtils.add(qcOrderInfo.getPassQty(), qcOrderDetail.getPassQty()));
this.updateById(qcOrderInfo);
@Override
public boolean updateById(CkQcOrderInfo entity) {
    entity.setUpdateUser(UserUtils.getCurrentUserId());
    entity.setUpdateTime(LocalDateTime.now());
    return super.updateById(entity);
}
View Code

Mybatis-in

<select id="getSimilarity" parameterType="java.util.HashMap" resultType="java.util.HashMap">
    select * from table
    where ids in 
    <foreach item="item" index="index" collection="ids.split(',')"  open="(" separator="," close=")">
                #{item}
    </foreach>
  </select>
View Code

Mybatis-time-between

<if test="xx.startTime != null">
    AND create_time &gt;= #{xx.startTime}
</if>
<if test="xx.endTime != null">
    AND create_time &lt;= #{xx.endTime}
</if>
View Code

 

其它

Idea代碼模板

// 1.toPageVo -- $beanName$從clipboard()獲取
private IPage<$beanName$VO> toPageVo(IPage<$beanName$> page) {
    return new Page<$beanName$VO>(page.getCurrent(), page.getSize(), page.getTotal())
        .setRecords(page.getRecords().stream().map(m -> beanToVo(m)).collect(Collectors.toList()));
}
private $beanName$VO beanToVo($beanName$ m) {
    if (Objects.isNull(m)) {
        return null;
    }
    $beanName$VO vo = new $beanName$VO();
    BeanUtils.copyProperties(m, vo);
    $END$
    return vo;
}
// 2.ifeq:判斷相等
if (Objects.equals($v1$, $v2$)) {
    $END$
}
// 3.ifn:判斷空
if (Objects.isNull($VAR$)) {
    $END$
}
// 4.pstr:private String
/**
 * $desc$
 */
@ApiModelProperty(value = "$desc$")
private String $v$;
$END$
// 5.psdate
/**
 * 起始時間
 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "起始時間")
private LocalDateTime $END$startTime;
// 6.pedate
/**
 * 結束時間
 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "結束時間")
private LocalDateTime $END$endTime;
// 7.plist
/**
 * $desc$
 */
@ApiModelProperty(value = "$desc$")
private List<$type$> items;
$END$
// 8.tcc
try {
    $END$
} catch(Exception e) {
    e.printStackTrace();
}
View Code

hutool工具類

官網

JDK8常規操作

public class Apple {
    private Integer id;
    private String name;
    private BigDecimal money;
    private Integer num;
    public Apple(Integer id, String name, BigDecimal money, Integer num) {
        this.id = id;
        this.name = name;
        this.money = money;
        this.num = num;
    }
}

// 測試數據
List<Apple> appleList = new ArrayList<>();//存放apple對象集合
Apple apple1 =  new Apple(1,"蘋果1",new BigDecimal("3.25"),10);
Apple apple12 = new Apple(1,"蘋果2",new BigDecimal("1.35"),20);
Apple apple2 =  new Apple(2,"香蕉",new BigDecimal("2.89"),30);
Apple apple3 =  new Apple(3,"荔枝",new BigDecimal("9.99"),40);
appleList.add(apple1);
appleList.add(apple12);
appleList.add(apple2);
appleList.add(apple3);

//1.List轉Map
/**
* List -> Map
* 需要注意的是:
* toMap 如果集合對象有重復的key,會報錯Duplicate key ....
*  apple1,apple12的id都為1。
*  可以用 (k1,k2)->k1 來設置,如果有重復的key,則保留key1,舍棄key2
*/
Map<Integer, Apple> appleMap = appleList.stream().collect(Collectors.toMap(Apple::getId, a -> a,(k1,k2)->k1));

//2.分組
//List 以ID分組 Map<Integer,List<Apple>>
Map<Integer, List<Apple>> groupBy = appleList.stream().collect(Collectors.groupingBy(Apple::getId));

System.err.println("groupBy:"+groupBy);
{1=[Apple{id=1, name='蘋果1', money=3.25, num=10}, Apple{id=1, name='蘋果2', money=1.35, num=20}], 2=[Apple{id=2, name='香蕉', money=2.89, num=30}], 3=[Apple{id=3, name='荔枝', money=9.99, num=40}]}

//3.過濾filter
//過濾出符合條件的數據
List<Apple> filterList = appleList.stream().filter(a -> a.getName().equals("香蕉")).collect(Collectors.toList());

System.err.println("filterList:"+filterList);
[Apple{id=2, name='香蕉', money=2.89, num=30}]

//4.求和
//計算 總金額
BigDecimal totalMoney = appleList.stream().map(Apple::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
System.err.println("totalMoney:"+totalMoney);  //totalMoney:17.48

//計算 數量int sum = appleList.stream().mapToInt(Apple::getNum).sum();
System.err.println("sum:"+sum);  //sum:100

// 5.stream下標問題
AtomicInteger sort = new AtomicInteger(1);
list = list.stream().map(m -> {
    m.setImgSort(sort.get());
    sort.incrementAndGet();
    return m;
}).collect(Collectors.toList());

// 6.LocalDate常用
LocalDate date = LocalDate.of(2014, 3, 18);
DayOfWeek dow = date.getDayOfWeek(); // -》TUESDAY
int len = date.lengthOfMonth(); // -》31
boolean leap = date.isLeapYear(); // -》false (not a leap year)

LocalTime time = LocalTime.of(13, 45, 20); //-》13:45:20
LocalTime time = LocalTime.parse("13:45:20");
LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();

// Date轉LocalDateTime
Date date = new Date();
LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

// 相差天數
long days = ChronoUnit.DAYS.between(LocalDate.of(2014, 3, 8), LocalDate.of(2014, 4, 18));

// 日期比較 isBefore、isAfter
LocalDate t1 = LocalDate.of(2020, 2, 1);
LocalDate t2 = LocalDate.of(2020, 3, 2);
System.out.println(t1.isBefore(t2) + t1.isAfter(t2));

// LocalDateTime GET方式請求數據如何接收
/** 結算起始時間 */
@ApiModelProperty(value = "結算起始時間")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startTime;
/** 結算結束時間 */
@ApiModelProperty(value = "結算結束時間")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endTime;

// 當月第一天、最后一天
LocalDateTime date = LocalDateTime.now();
LocalDateTime firstday = date.with(TemporalAdjusters.firstDayOfMonth());
LocalDateTime lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
System.out.println(“firstday:” + firstday);
System.out.println(“lastDay:” + lastDay);
View Code

常用正則工具類

/**
 * 1.按正則匹配字符串某一個子串
 */
// "@pms.hasPermission('cg_cgshiperrorder_edit')" -> cg_cgshiperrorder_edit
private final static Pattern pattern = Pattern.compile("@pms.hasPermission\\('(.*?)'\\)");
private static String getPermission(String str) {
    Matcher m = pattern.matcher(str);
    String result = null;
    while (m.find()) {
        result = m.group(1);
        break;
    }
    return result;
}

// 2.否定先行斷言 (?!pattern)
// a(?!b) -> 匹配后面不是跟着 "b" 的 "a"
    
// 3.指定正則表達式的模式
// (?i) -> 使正則忽略大小寫
// (?s) -> 使正則的 . 匹配所有字符,包括換行符。
// (?m) -> 使正則的 ^ 和 $ 匹配字符串中每行的開始和結束。

// 4.橫向匹配
var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log( string.match(regex) ); // ["a1b", "a2b", "a3b"]

// 5.縱向模糊匹配
var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log( string.match(regex) ); // ["a1b", "a2b", "a3b"]

// 6.范圍[123456abcdefGHIJKLM] -> [1-6a-fG-M]
// 因為連字符有特殊用途,那么要匹配“a”、“-”、“z”這三者中任意一個字符 -> [-az]或[az-]或[a\-z]

// 7.排除字符組 [^abc] -> 某位字符可以是任何東西,但就不能是"a"、"b"、"c"

// 8.常見的簡寫形式
// \d -> [0-9] 數字
// \D -> [^0-9] 非數字
// \w -> [0-9a-zA-Z_] 單詞
// \W -> [^0-9a-zA-Z_] 非單詞
// \s -> [ \t\v\n\r\f] 空白
// \S -> [^ \t\v\n\r\f] 非空白
// . -> [^\n\r\u2028\u2029] 通配符,表示幾乎任意字符。換行符、回車符、行分隔符和段分隔符除外。
// 匹配任意字符 [\d\D]、[\w\W]、[\s\S]、和[^]中任何的一個

// 9.量詞
// {m,} -> 至少出現m次
// {m} -> 出現m次
// ? -> 0或1次
// + -> 至少出現1次
// * -> 出現任意次,有可能不出現
View Code

Junit測試

(1) 常用測試

// 1.正常測試
@Test
public void test_detail() {
    CkQcOrderInfoDetailVO vo = ckQcOrderInfoService.getDetailById(1L);
    assertTrue("成功", Objects.isNull(vo.getId()));
}

// 2.異常測試
@Test
public void test_qc() {
    try {
        CkQcOrderDetailDTO qcOrderDetail = new CkQcOrderDetailDTO();
        qcOrderDetail.setQcOrderId(1L);
        qcOrderDetail.setQcQty(5);
        qcOrderDetail.setPassQty(5);
        ckQcOrderInfoService.qc(qcOrderDetail);
    } catch (IllegalArgumentException e) {
        assertThat(e.getMessage(), containsString("單號不存在或已刪除!"));
    }
    fail("單號不存在或已刪除!");
}

(2) springboot test

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@AutoConfigureMockMvc
@Slf4j
//@Transactional
public class BaseTest {
    @Autowired
    private WebApplicationContext context;

    protected MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders
            .webAppContextSetup(context)
            .build();
    }

}

(3) REST接口測試

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@Slf4j
public class OAuthMvcTest {

    protected HttpMethod GET = HttpMethod.GET;
    protected HttpMethod POST = HttpMethod.POST;
    protected HttpMethod PUT = HttpMethod.PUT;
    protected HttpMethod DELETE = HttpMethod.DELETE;

    @Autowired
    private TestRestTemplate restTemplate;
    final static String API_URL = "http://localhost:9999/admin";
    protected static String USRE_NAME = "admin";
    protected static String PASSWORD = "123456";

    @Before
    public void setup() {
    }

    @Test
    public void test_access_token_then200() {
        String token = obtainAccessToken("admin", "123456");
        log.info("access_token->{}", token);
    }

    protected String execute(String requestControllerMethodPath, HttpMethod method, Object param) {
        return execute(requestControllerMethodPath, method, param, String.class);
    }

    /**
     * 執行測試用例
     * @param requestControllerMethodPath controller+method請求路徑
     * @param method HttpMethod.GET|POST|PUT|DELETE
     * @param param 請求參數,controller里面的對象
     */
    protected <T> T execute(String requestControllerMethodPath, HttpMethod method, Object param, Class<T> clazz) {
        return execute(requestControllerMethodPath, method, param, USRE_NAME, PASSWORD, clazz);
    }

    /**
     * 執行測試用例
     * @param requestControllerMethodPath controller+method請求路徑
     * @param method HttpMethod.GET|POST|PUT|DELETE
     * @param param 請求參數,controller里面的對象
     * @param username 用戶名
     * @param password 密碼
     */
    protected <T> T execute(String requestControllerMethodPath, HttpMethod method, Object param, String username, String password, Class<T> clazz) {
        if (!requestControllerMethodPath.startsWith("/")) {
            requestControllerMethodPath = "/" + requestControllerMethodPath;
        }
        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.AUTHORIZATION, OAuth2AccessToken.BEARER_TYPE + " " + obtainAccessToken(username, password));
        HttpEntity entity = new HttpEntity<>(param, headers);
        String endpoint = API_URL + requestControllerMethodPath;
        log.info("request->{}" + entity);
        ResponseEntity responseEntity = restTemplate.exchange(endpoint, method, entity, clazz);
        log.info("body->{}", responseEntity.getBody());
        TestCase.assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
        if (null == responseEntity.getBody()) {
            return null;
        }
        return (T) responseEntity.getBody();
    }

    /**
     * 獲取登錄token
     * @param username 用戶名
     * @param password 密碼
     * @return
     */
    protected String obtainAccessToken(String username, String password) {
        return obtainAccessInfo(username, password).getStr("access_token");
    }

    protected JSONObject obtainAccessInfo(String username, String password) {
        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.AUTHORIZATION, "Basic dGVzdDp0ZXN0");
        HttpEntity entity = new HttpEntity<>(null, headers);
        String endpoint = "http://localhost:9999/auth/oauth/token?username="+ username + "&password="+ password +"&randomStr=77371563933589077&code=dw2m&grant_type=password&scope=server";
        ResponseEntity responseEntity = restTemplate.exchange(endpoint, HttpMethod.POST, entity, String.class);
        log.info("token->{}", responseEntity.getBody());
        assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
        JSONObject userInfo = JSONUtil.parseObj(responseEntity.getBody());
        return userInfo;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM