实习学习记录

SQL的Case When

结合sum和count的case when

CASE
when A = B then "未修改"
when A != B then "已修改"
end
sum(
    case 
    when A=B then 1 
    else 0 
    end
)
**简单CASE函数**
COUNT(
    CASE state
    WHEN 1 THEN 1
    ELSE 0 END 
) AS countAmt
 
 
**CASE搜索函数**
COUNT(
    CASE 
    WHEN state = '1' THEN 1
    ELSE 0 END 
) AS countAmt

HttpClient

构建流程:

  1. 创建客户端连接对象CloseableHttpClient httpclient

  2. 创建URIBuilder uriBuilder对象;

  3. 定义一个list,该list的数据类型是NameValuePair(简单名称值对节点类型),存放Get/Post请求的参数;

  4. 使用uriBuilder.setParameters(list)进行参数拼接;

  5. uriBuilder.build()获取URL;

  6. 创建HttpGet Get/HttpPost Post请求;

  7. 发送Get请求CloseableHttpResponse response=httpclient.execute(httpGet)获取响应模型;

  8. 从响应模型中获取响应实体HttpEntity entity;

  9. 释放连接资源。

public class httpClientUtil {
    //创建客户端连接对象
    private static final CloseableHttpClient httpclient = HttpClients.createDefault();

    /**
     * 发送HttpGet带参请求
     * @param url
     * @param header
     * @return
     */
    public static String sendGet(String url, Map header) {
        String result = null;
        CloseableHttpResponse response = null;

        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            //定义了一个list,该list的数据类型是NameValuePair(简单名称值对节点类型)
            //这个代码多处用于Java向url发送Get/Post请求,在发送Get/post请求时用该list来存放参数
            List list = new LinkedList<>();
            for(Map.Entry entry: header.entrySet()){
                list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));

            }
            uriBuilder.setParameters(list);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            //设置头部
//            httpGet.setHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"));
//            httpGet.setHeader(new BasicHeader("Accept", "text/plain;charset=utf-8"));
            //设置头部
//            for(Map.Entry entry: header.entrySet()){
//                httpGet.setHeader(entry.getKey().toString(),entry.getValue().toString());
//            }
            try {
                response = httpclient.execute(httpGet);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    result = EntityUtils.toString(entity);
                }
            } catch (ParseException | IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    httpclient.close();
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        return result;
    }
}

SpringBoot单测

  • 单元测试类在测试包test下的路径与类路径Java下的路径最好一致;

  • 如果包目录不一致,则需使用注解@SpringBootTest(classes = xxx.class)指定启动类

Java关键字assert的语法

assert 

如果为true,则程序继续执行。
如果为false,则程序抛出AssertionError,并终止执行。

------------------------------------------------------

assert  : <错误信息表达式>

如果为true,则程序继续执行。
如果为false,则程序抛出java.lang.AssertionError,并输入<错误信息表达式>。

 Spring提供的Assert类

Assert断言基本上替换传统的if判断,减少业务参数校验的代码行数,提高程序可读性。

使用Assert类的方法,如果检查通过则程序继续往下运行,如果不通过,则中断并抛出异常信息。

Assert.assertEquals(预期值,实际值); //比较实际值与预期值是否相等
Assert.assertNull(变量);  //判断参数是否为Null

MockMvc

  • 在面向对象的程序设计中,模拟对象(英语:mock object)是以可控的方式模拟真实对象行为的假对象。在编程过程中,通常通过模拟一些输入数据,来验证程序是否达到预期结果
  • MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,对于结果的验证十分方便。
  • 接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilderDefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。
// 使用standaloneSetup方法实例化
//注意,如果AA类有@ConfigurationProperties注解,该方式无法获取配置文件相应属性
mockMvc = MockMvcBuilders.standaloneSetup(new AA()).build();

// 使用webAppContextSetup方法实例化
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
/**
1. mockMvc.perform:执行一个请求
2. MockMvcRequestBuilders.get("XXX"):构造一个get请求
3. ResultActions.andExpect添加执行完成后的断言
4. ResultActions.andReturn表示执行完成后返回相应的结果
5. ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
*/
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(url)
                      .session(session).contentType("application/json"))
                      .andExpect(status().isOk()).andReturn();

JaCoCo

JaCoCo是一个开源的覆盖率工具,包含了多种尺度的覆盖率计数器,包含指令级覆盖(Instructions, C0coverage),分支(Branches, C1coverage)、圈复杂度(Cyclomatic Complexity)、行覆盖(Lines)、方法覆盖(non-abstract methods)、类覆盖(classes)。

jacoco支持多种覆盖率的统计,包括:

  1. 行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行。

  2. 类覆盖率:度量计算class类文件是否被执行。

  3. 分支覆盖率:度量if和switch语句的分支覆盖情况,计算一个方法里面的总分支数,确定执行和不执行的 分支数量。

  4. 方法覆盖率:度量被测程序的方法执行情况,是否执行取决于方法中是否有至少一个指令被执行。

  5. 指令覆盖:计数单元是单个java二进制代码指令,指令覆盖率提供了代码是否被执行的信息,度量完全 独立源码格式。

  6. 圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目,缺失的复杂度同样表示测试案例没有完全覆盖到这个模块。

在pom.xml文件中添加JaCoCo插件配置如下:


    org.jacoco
    jacoco-maven-plugin
    ${jacoco.version}
    
        
        
            com/aa/bb/cc/service/inter/impl/StarServiceImpl*
        
    
    
    
        
        
            pre-test
            
                prepare-agent
            
            
                jacocoArgLine
            
        
        
        
            post-unit-test
            test
            
                report
            
            
                
                target/coverage-reports/jacoco-ut
                
                
                target/coverage-reports/jacoco-unit.exec
             
        
    


    maven-surefire-plugin
    3.0.0-M7
    
        
             -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco.version}/org.jacoco.agent-${jacoco.version}-runtime.jar=destfile=${project.basedir}/target/coverage-reports/jacoco-unit.exec
        
        
        
            
            **/*StarServiceImplTest.java
            **/*Spec
        
    

WebApplicationContext

  1. Spring容器负责管理Bean与Bean之间的依赖关系,Spring容器最基本的接口就是BeanFactory,BeanFactory负责配置、创建、管理Bean
  2. ApplicationContext由BeanFactory派生而来,BeanFactory的许多功能需要编程实现,而在ApplicationContext中则可以通过配置的方式实现;
  3. ApplicationContext因此也称之为Spring上下文
  • WebApplicationContext 接口扩展了ApplicationContext ,它是专门为 Web 应用设计的,但并意味着只有它才能为 Web应用服务;

  • WebApplicationContext是专门为web应用准备的,他允许从相对于web根目录的路径中装载配置文件完成初始化工作;

  • WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便web应用可以访问spring上下文

  • spring中提供WebApplicationContextUtilsgetWebApplicationContext(ServletContext src)方法来获得WebApplicationContext对象

stream流的map()方法

  • 函数式编程简单理解就是将方法作为参数传入,能够提高编写效率,减少代码冗余量
  • stream()是将list里面的数据变成流的形式,然后将每个list中的每个值传入到map中的方法中去并通过collect(Collectors.toList())构建成新的list
public class tt {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        List listAdd = list.stream().map(s -> s + 2).collect(Collectors.toList());
        System.out.println(listAdd);
        //tt::add2 调用tt类的add2方法
        List listAdd02 = list.stream().map(tt::add2).collect(Collectors.toList());
        System.out.println(listAdd02);
    }

    private static int add2(Integer temp){
        return  temp + 2;
    }
}

一些注解

@Async

@Async注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。

同步与异步的概念:

  • 同步:整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法,如果它们都是同步调用,则需要将它们都顺序执行完毕之后,才算作过程执行完毕;

  • 异步:只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕,而是继续执行下面的流程。例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法,如果B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。

  • 在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的业务子线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。

异步的方法有:

  1. 最简单的异步调用,返回值为void

  2. 带参数的异步调用,异步方法可以传入参数

  3. 存在返回值,常调用返回Future

@Async自定义线程池的方式:

  • 重新实现接口AsyncConfigurer

  • 继承AsyncConfigurerSupport

  • 配置由自定义的TaskExecutor替代内置的任务执行器

@Builder和@SuperBuilder

使用@Builder注解的优点:

  1. 代替若干参数情况下的构造函数,减少代码量;

  2. 通过Builder构造的方式,即.属性名(值)的方式,比直接使用构造函数的方式更具备可读性,比频繁使用set方式更简洁

public class builderStudy {
    Ming mingA = Ming.builder().build();
    Ming mingB = Ming.builder().age(11).name("BB").build();
}

@Builder
class Ming{
    private int age;
    private String name;
}
  1. 如果@Builder修饰的类是某个类的子类,那么之前调用的.builder()会报错,这是因为@Builder并不支持父类成员属性的构造

  2. @SuperBuilder可以解决这个问题,这样子类就可以正常获取到父类的成员属性进行builder构造了

实习学习记录_第1张图片

public class builderStudy {
    Ming mingA = Ming.builder().build();
    Ming mingB = Ming.builder().age(11).name("BB").build();
}

@SuperBuilder
class Ming extends Person{

}

@SuperBuilder
class Person{
    private Integer age;
    private String name;
}

@SpringBootTest和@RunWith和@Test

@SpringBootTest 自动侦测并加载@SpringBootApplication或@SpringBootConfiguration中的配置,默认web环境为MOCK,不监听任务端口。使用 @SpringBootTest 后,Spring 将加载所有被管理的 bean,基本等同于启动了整个服务,此时便可以开始功能测试。

  • 如果单元测试类在测试包test下的路径与类路径Java下的路径一致的话,使用@SpringBootTest即可

  • 如果不一致,则使用@SpringBootTest(classes = xxx.class)指定启动类,启动spring容器,加载spring上下文ApplicationContext

@RunWith是 Junit4 提供的注解,如果测试类使用的是 Junit4 的 org.junit.Test,则需要加上@RunWith(SpringRunner.class),如果没有的话,将导致service、dao等自动注入失败

@RunWith(SpringRunner.class)

  1. 指定Runner运行器;

  2. 与Spring环境整合

假如测试方法需要注入bean即需要spring环境,就在类上把@SpringBootTest和@RunWith这两个注解都带上

在方法上使用@Test注解,表示该方法为一个测试方法,可以不用main方法调用就可以测试出运行结果

注意:被测试的方法必须是public修饰的

@AutoWired和@Resource

自动装配是什么呢?简单来说:Spring 利用依赖注入(DI)功能,完成Spring IOC容器中各个组件之间的依赖关系赋值管理。

IOC操作Bean管理,bean管理是指(1)spring创建对象 (2)spring注入属性。当我们在将一个类上标注@Service或者@Controller或@Component或@Repository注解之后,spring的组件扫描就会自动发现它,并且会将其初始化为spring应用上下文中的bean。 而且初始化是根据无参构造函数

@AutoWired  Spring 内置的注解

@Autowired 自动注入,将Spring IOC容器中已经注册好的对象注入到程序员定义的类型中,使其实例化(相当于new一个对象给定义的类型)并可用。@Autowired 可以对类成员变量(比较常见)、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。

@Autowired注解的注入规则:默认按照类型进行注入,如果找到的类型是唯一且匹配则返回这个对象并注入到用@Autowired标识的定义类型中,如果IOC容器中存在两个及以上的相同类型的bean时,则可以和@Qualifier搭配使用按照byName根据bean的名称进行注入,如果没有指定名称的bean,则会报错。

@Autowired
IAccountDao abc;

//1
@Service
public class AccountDao implements IAccountDao

//2
@Service(value = "abc")
public class AccountDao2 implements IAccountDao
  • @Autowired 首先会根据类型去Spring IOC容器去匹配与IAccountDao一致的类型,如果IAccountDao接口只有一个实现类AccountDao,由于匹配的类型是唯一的,那么向@Autowired标识的abc注入的即为AccountDao。

  • 如果IAccountDao接口有多个实现类AccountDao、AccountDao2,由于这两个实现类都实现了IAccountDao接口,很明显匹配到的类型是不唯一的,此时会根据属性的名称abc作为bean的id在IOC容器中查找,那么就可以搭配@Qualifier根据名称进行注入,直接指定要⾃动装配的bean的id。

@AutoWired
@Qualifier("abc")
IAccountDao iAccountDao

@Resource JDK 提供的注解

默认按照名称进行注入

@Resource(name = "abc")
IAccountDao iAccountDao

@Transactional

Spring事务管理

  • 方法:推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。
  • :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。

事务的传播行为(7种),关注Propagation.REQUIREDPropagation.REQUIRES_NEWPropagation.NESTED

Propagation.REQUIRED(默认)

如果当前没有事务,就新建一个事务;如果已存在一个事务中,加入到这个事务中,共进退。

Propagation.REQUIRES_NEW

如果当前存在事务,那么将当前的事务挂起,并开启一个新事务去执行。

外部方法transTest()调用内部方法saveParent()(每次执行完都将info表和person表置为空)

外部方法用Propagation.REQUIRED修饰内部方法用Propagation.REQUIRES_NEW修饰

  • 外部方法transTest()抛出异常,内部方法saveParent()正常
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
    //向info表插入数据
    String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
    List batchArgs=new ArrayList<>();
    batchArgs.add(new Object[]{1,"123", "china"});
    jdbcTemplate.batchUpdate(insertQuery, batchArgs);
    personService.saveParent(1, "A", 42);
    int a = 10 / 0;
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveParent(int id, String name, int age) {
    //向person表插入数据
    personMapper.insertPerson(id, name, age);

}

结果:外部方法回滚,内部方法正常提交

实习学习记录_第2张图片

  • 外部方法transTest()正常,内部方法saveParent()抛出异常
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
    //向info表插入数据
    String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
    List batchArgs=new ArrayList<>();
    batchArgs.add(new Object[]{1,"123", "china"});
    jdbcTemplate.batchUpdate(insertQuery, batchArgs);
    personService.saveParent(1, "A", 42);
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveParent(int id, String name, int age) {
    //向person表插入数据
    personMapper.insertPerson(id, name, age);
    int a = 10 / 0;
}

结果:内部方法回滚,外部方法也回滚

实习学习记录_第3张图片

Propagation.NESTED

如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,就执行与Propagation.REQUIRED类似的操作,即新建事务。

外部方法用Propagation.REQUIRED修饰内部方法用Propagation.NESTED修饰

  • 外部方法transTest()抛出异常,内部方法saveParent()正常
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
    //向info表插入数据
    String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
    List batchArgs=new ArrayList<>();
    batchArgs.add(new Object[]{1,"123", "china"});
    jdbcTemplate.batchUpdate(insertQuery, batchArgs);
    personService.saveParent(1, "A", 42);
    int a = 10 / 0;
}

@Override
@Transactional(propagation = Propagation.NESTED)
public void saveParent(int id, String name, int age) {
    //向person表插入数据
    personMapper.insertPerson(id, name, age);

}

结果:内部方法回滚,外部方法也回滚

实习学习记录_第4张图片

  • 外部方法transTest()正常,内部方法saveParent()抛出异常
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
    //向info表插入数据
    String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
    List batchArgs=new ArrayList<>();
    batchArgs.add(new Object[]{1,"123", "china"});
    jdbcTemplate.batchUpdate(insertQuery, batchArgs);
    personService.saveParent(1, "A", 42);
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveParent(int id, String name, int age) {
    //向person表插入数据
    personMapper.insertPerson(id, name, age);
    int a = 10 / 0;
}

结果:内部方法回滚,外部方法也回滚

实习学习记录_第5张图片

小结一下:

  • 区别在于,Propagation.REQUIRES_NEW的态度是外界纷纷扰扰与我何干,而Propagation.NESTED的态度是父子连坐
  • 相同的是,内部出了问题,外部连坐

@JSONField

  • @JSONField是fastjson的一个注解,在fastjson解析一个类为Json对象时,作用到类的每一个属性(field)上
  • 将一个类序列化为JSON对象时,通过使用@JSONField注解可以实现定义key等操作
public class Student {
    @JSONField(name = "student_name")
    private String name;

    @JSONField(name = "student_age")
    private int age;

    @JSONField(name = "student_id")
    private int id;

    @JSONField(name = "student_address")
    private String address;

    @JSONField(name = "student_phone_number")
    private String phoneNumber;
}
    public void stu(){
        Student stu = Student.builder().name("A").age(12).id(1).address("BB").address("1212").build();
        System.out.println(stu.toString());
        String jsonString = JSON.toJSONString(stu);
        System.out.println(jsonString);
    }

@NotBlank

字段校验注解

  • @NotNull:不能为 null,但可以为 empty,一般用在 Integer 类型的基本数据类型的非空校验上,而且被其标注的字段可以使用 @size、@Max、@Min 对字段数值进行大小的控制

  • @NotEmpty:不能为 null,且长度必须大于 0,一般用在集合类上或者数组上

  • @NotBlank:只能作用在String上,不能为null,而且调用trim()后,长度必须大于0

@ConfigurationProperties

@ConfigurationProperties需要和@Configuration配合使用

@Configuration
@ConfigurationProperties(prefix = "aa")
@Data
public class AAConfig {
  private String id;
  private String key;
  private String address;
}
aa:
  id: liu
  key: xxx
  address: baibai

上面的例子将会读取yml文件中所有以aa开头的属性,并和bean中的字段进行匹配

@Data

@Data可以标注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法

作用:提高代码的简洁,使用这个注解可以省去代码中大量的get()、 set()、 toString()等方法

  • @Setter : 注在属性上,提供 set 方法

  • @Getter : 注在属性上,提供 get 方法

你可能感兴趣的:(学习,java)