结合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
构建流程:
创建客户端连接对象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 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;
}
}
单元测试类在测试包test下的路径与类路径Java下的路径最好一致;
如果包目录不一致,则需使用注解@SpringBootTest(classes = xxx.class)指定启动类
assert
如果为true,则程序继续执行。
如果为false,则程序抛出AssertionError,并终止执行。
------------------------------------------------------
assert : <错误信息表达式>
如果为true,则程序继续执行。
如果为false,则程序抛出java.lang.AssertionError,并输入<错误信息表达式>。
Assert断言基本上替换传统的if判断,减少业务参数校验的代码行数,提高程序可读性。
使用Assert类的方法,如果检查通过则程序继续往下运行,如果不通过,则中断并抛出异常信息。
Assert.assertEquals(预期值,实际值); //比较实际值与预期值是否相等
Assert.assertNull(变量); //判断参数是否为Null
- 在面向对象的程序设计中,模拟对象(英语: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是一个开源的覆盖率工具,包含了多种尺度的覆盖率计数器,包含指令级覆盖(Instructions, C0coverage),分支(Branches, C1coverage)、圈复杂度(Cyclomatic Complexity)、行覆盖(Lines)、方法覆盖(non-abstract methods)、类覆盖(classes)。
jacoco支持多种覆盖率的统计,包括:
行覆盖率:度量被测程序的每行代码是否被执行,判断标准行中是否至少有一个指令被执行。
类覆盖率:度量计算class类文件是否被执行。
分支覆盖率:度量if和switch语句的分支覆盖情况,计算一个方法里面的总分支数,确定执行和不执行的 分支数量。
方法覆盖率:度量被测程序的方法执行情况,是否执行取决于方法中是否有至少一个指令被执行。
指令覆盖:计数单元是单个java二进制代码指令,指令覆盖率提供了代码是否被执行的信息,度量完全 独立源码格式。
圈复杂度:在(线性)组合中,计算在一个方法里面所有可能路径的最小数目,缺失的复杂度同样表示测试案例没有完全覆盖到这个模块。
在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
- 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()是将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注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
同步与异步的概念:
同步:整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法,如果它们都是同步调用,则需要将它们都顺序执行完毕之后,才算作过程执行完毕;
异步:只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕,而是继续执行下面的流程。例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法,如果B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。
在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的业务子线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。
异步的方法有:
最简单的异步调用,返回值为void
带参数的异步调用,异步方法可以传入参数
存在返回值,常调用返回Future
@Async自定义线程池的方式:
重新实现接口AsyncConfigurer
继承AsyncConfigurerSupport
配置由自定义的TaskExecutor替代内置的任务执行器
使用@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 自动侦测并加载@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修饰的
自动装配是什么呢?简单来说: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
Spring事务管理
事务的传播行为(7种),关注Propagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.NESTED
Propagation.REQUIRED(默认)
如果当前没有事务,就新建一个事务;如果已存在一个事务中,加入到这个事务中,共进退。
Propagation.REQUIRES_NEW
如果当前存在事务,那么将当前的事务挂起,并开启一个新事务去执行。
外部方法transTest()调用内部方法saveParent()(每次执行完都将info表和person表置为空)
外部方法用Propagation.REQUIRED修饰,内部方法用Propagation.REQUIRES_NEW修饰
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
//向info表插入数据
String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
List
结果:外部方法回滚,内部方法正常提交
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
//向info表插入数据
String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
List
结果:内部方法回滚,外部方法也回滚
Propagation.NESTED
如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,就执行与Propagation.REQUIRED类似的操作,即新建事务。
外部方法用Propagation.REQUIRED修饰,内部方法用Propagation.NESTED修饰
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
//向info表插入数据
String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
List
结果:内部方法回滚,外部方法也回滚
@Test
@Transactional(propagation = Propagation.REQUIRED)
public void transTest(){
//向info表插入数据
String insertQuery = "insert into info (id,phone,address) values (?,?,?)";
List
结果:内部方法回滚,外部方法也回滚
小结一下:
- 区别在于,Propagation.REQUIRES_NEW的态度是外界纷纷扰扰与我何干,而Propagation.NESTED的态度是父子连坐
- 相同的是,内部出了问题,外部连坐
- @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);
}
字段校验注解
@NotNull:不能为 null,但可以为 empty,一般用在 Integer 类型的基本数据类型的非空校验上,而且被其标注的字段可以使用 @size、@Max、@Min 对字段数值进行大小的控制
@NotEmpty:不能为 null,且长度必须大于 0,一般用在集合类上或者数组上
@NotBlank:只能作用在String上,不能为null,而且调用trim()后,长度必须大于0
@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可以标注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法
作用:提高代码的简洁,使用这个注解可以省去代码中大量的get()、 set()、 toString()等方法
@Setter : 注在属性上,提供 set 方法
@Getter : 注在属性上,提供 get 方法