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
构建流程:
创建客户端连接对象CloseableHttpClient httpclient;
创建URIBuilder uriBuilder对象;
定义一个list,该list的数据类型是NameValuePair(简单名称值对节点类型),存放Get/Post请求的参数;
使用uriBuilder.setParameters(list)进行参数拼接;
uriBuilder.build()获取URL;
创建HttpGet Get/HttpPost Post请求;
发送Get请求CloseableHttpResponse response=httpclient.execute(httpGet)获取响应模型;
从响应模型中获取响应实体HttpEntity entity;
释放连接资源。
public class httpClientUtil {
//创建客户端连接对象
private static final CloseableHttpClient httpclient = HttpClients.createDefault();
/**
* 发送HttpGet带参请求
* @param url
* @param header
* @return
*/
public static String sendGet(String url, Map<String, String> header) {
String result = null;
CloseableHttpResponse response = null;
try {
URIBuilder uriBuilder = new URIBuilder(url);
//定义了一个list,该list的数据类型是NameValuePair(简单名称值对节点类型)
//这个代码多处用于Java向url发送Get/Post请求,在发送Get/post请求时用该list来存放参数
List<NameValuePair> list = new LinkedList<>();
for(Map.Entry<String,String> 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 <boolean表达式>
如果<boolean表达式>为true,则程序继续执行。
如果为false,则程序抛出AssertionError,并终止执行。
------------------------------------------------------
assert <boolean表达式> : <错误信息表达式>
如果<boolean表达式>为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。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成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支持多种覆盖率的统计,包括:
-
行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行。
-
类覆盖率:度量计算class类文件是否被执行。
-
分支覆盖率:度量if和switch语句的分支覆盖情况,计算一个方法里面的总分支数,确定执行和不执行的 分支数量。
-
方法覆盖率:度量被测程序的方法执行情况,是否执行取决于方法中是否有至少一个指令被执行。
-
指令覆盖:计数单元是单个java二进制代码指令,指令覆盖率提供了代码是否被执行的信息,度量完全 独立源码格式。
-
圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目,缺失的复杂度同样表示测试案例没有完全覆盖到这个模块。
在pom.xml文件中添加JaCoCo插件配置如下:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<!--针对符合下面的命名模式的测试类生成覆盖率报告-->
<includes>
<include>com/aa/bb/cc/service/inter/impl/StarServiceImpl*</include>
</includes>
</configuration>
<!--在<executions>中配置执行步骤:
1)prepare-agent(即构建jacoco-unit.exec);
2)check(即根据在<rules>定义的规矩进行检测);
3)package(生成覆盖率报告)
-->
<executions>
<!-- 在maven的initialize阶段,将Jacoco运行时代理的属性作为VM的一个参数传给被测程序,用于监控JVM中的调用-->
<execution>
<id>pre-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>jacocoArgLine</propertyName>
</configuration>
</execution>
<!--report:对代码进行检测,然后生成index.html-->
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!--覆盖率报告的输出位置-->
<outputDirectory>target/coverage-reports/jacoco-ut</outputDirectory>
<!--指定生成.exec文件的存放位置-->
<!--Jacoco是根据.exec文件生成最终的报告,所以需指定.exec的存放路径-->
<dataFile>target/coverage-reports/jacoco-unit.exec</dataFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<argLine>
-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
</argLine>
<!--执行测试源码路径(默认为sec/test/java/)下所有符合下面的命名模式的测试类-->
<includes>
<!--任何子目录下所有命名以StarServiceImplTest结尾的java类-->
<include>**/*StarServiceImplTest.java</include>
<include>**/*Spec</include>
</includes>
</configuration>
</plugin>
WebApplicationContext
- Spring容器负责管理Bean与Bean之间的依赖关系,Spring容器最基本的接口就是BeanFactory,BeanFactory负责配置、创建、管理Bean;
- ApplicationContext由BeanFactory派生而来,BeanFactory的许多功能需要编程实现,而在ApplicationContext中则可以通过配置的方式实现;
- ApplicationContext因此也称之为Spring上下文。
-
WebApplicationContext 接口扩展了ApplicationContext ,它是专门为 Web 应用设计的,但并意味着只有它才能为 Web应用服务;
-
WebApplicationContext是专门为web应用准备的,他允许从相对于web根目录的路径中装载配置文件完成初始化工作;
-
从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便web应用可以访问spring上下文
-
spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext src)方法来获得WebApplicationContext对象
stream流的map()方法
- 函数式编程简单理解就是将方法作为参数传入,能够提高编写效率,减少代码冗余量
- stream()是将list里面的数据变成流的形式,然后将每个list中的每个值传入到map中的方法中去并通过collect(Collectors.toList())构建成新的list
public class tt {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Integer> listAdd = list.stream().map(s -> s + 2).collect(Collectors.toList());
System.out.println(listAdd);
//tt::add2 调用tt类的add2方法
List<Integer> 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中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的业务子线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。
异步的方法有:
-
最简单的异步调用,返回值为void
-
带参数的异步调用,异步方法可以传入参数
-
存在返回值,常调用返回Future
@Async自定义线程池的方式:
-
重新实现接口AsyncConfigurer
-
继承AsyncConfigurerSupport
-
配置由自定义的TaskExecutor替代内置的任务执行器
@Builder和@SuperBuilder
使用@Builder注解的优点:
代替若干参数情况下的构造函数,减少代码量;
通过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;
}
如果@Builder修饰的类是某个类的子类,那么之前调用的.builder()会报错,这是因为@Builder并不支持父类成员属性的构造
@SuperBuilder可以解决这个问题,这样子类就可以正常获取到父类的成员属性进行builder构造了
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)
-
指定Runner运行器;
-
与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.REQUIRED、Propagation.REQUIRES_NEW、Propagation.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<Object[]> 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);
}
结果:外部方法回滚,内部方法正常提交
- 外部方法transTest()正常,内部方法saveParent()抛出异常
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
//向info表插入数据
String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
List<Object[]> 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;
}
结果:内部方法回滚,外部方法也回滚
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<Object[]> 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);
}
结果:内部方法回滚,外部方法也回滚
- 外部方法transTest()正常,内部方法saveParent()抛出异常
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
//向info表插入数据
String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
List<Object[]> 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;
}
结果:内部方法回滚,外部方法也回滚
小结一下:
- 区别在于,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 方法