Java开发中有用的笔记

笔记

一、lambda表达式

1. ::和->是什么

“::” 和 “->” 都是Java 8中引入的Lambda表达式的一部分,用于简化代码和增强语言的函数式编程能力。

“::” 符号通常称为“方法引用”,用于引用已有的方法或构造函数,并将其作为Lambda表达式的参数。具体来说,方法引用可以将方法名和参数列表与类或对象的名称分开,以简化代码和提高可读性。方法引用的形式有以下几种:

  1. 引用静态方法:ClassName::staticMethodName
(x) -> ClassName.staticMethodName(x)

可以使用静态方法引用的方式进行简化:

ClassName::staticMethodName
  1. 引用实例方法:instanceName::methodName
(x) -> instanceName.methodName(x)

可以使用实例方法引用的方式进行简化:

instanceName::methodName
  1. 引用构造函数:ClassName::new
() -> new ClassName()

可以使用构造函数引用的方式进行简化:

ClassName::new
  1. “->” 符号通常称为“Lambda箭头”,用于定义Lambda表达式的语法结构。具体来说,Lambda表达式由参数列表、箭头符号和表达式组成,例如:
(x, y) -> x + y

其中,参数列表指定Lambda表达式的参数,箭头符号 “->” 分隔参数列表和表达式,表达式则指定Lambda表达式要执行的操作。

(x, y) -> {
    int sum = x + y;
    System.out.println(sum);
}

可以理解为:

  • 参数列表为 (x, y)
  • 箭头符号 “->” 分隔参数列表和表达式
  • 表达式为 { int sum = x + y; System.out.println(sum); }

需要注意的是,Lambda表达式和方法引用的使用都需要在Java 8及以上版本中才能实现。同时,还需要根据具体情况进行Lambda表达式的编写和调用,以确保代码的正确性和可读性。

2.foreach和.map

foreach 和 map 都是 Java 8 中的流操作,它们都可以帮助我们处理数据,但是它们有着明显的不同。

foreach 方法是一种遍历操作,它可以对流中的每一个元素执行某个操作,但是不会返回任何结果,它只是简单地对数据进行处理。

例如,我们有一个列表,列表中的每一个元素代表一个字符串,我们希望将列表中的所有字符串打印出来。我们可以使用 foreach 方法实现这个需求:

mathematicaCopy codeList list = Arrays.asList("apple", "banana", "cherry");
list.stream().forEach(System.out::println);

map 方法是一种映射操作,它可以对流中的每一个元素执行某个操作,并将操作的结果作为新的流的元素。

例如,我们有一个列表,列表中的每一个元素代表一个整数,我们希望将列表中的每一个整数乘以 2,并得到一个新的列表。我们可以使用 map 方法实现这个需求:

mathematicaCopy codeList list = Arrays.asList(1, 2, 3, 4, 5);
List result = list.stream().map(x -> x * 2).collect(Collectors.toList());

因此,foreach 和 map 都是 Java 8 中的流操作,它们都可以帮助我们处理数据,但是它们的目的和实现方式不同,需要根据具体的需求

map 方法与遍历的不同在于,它不仅仅遍历了流中的元素,还对每一个元素执行了一个操作,并将操作的结果作为新的流的元素。

因此,map 方法可以说是遍历,但是它不仅仅是遍历,它还是一种高效、易于使用、易于并行计算的数据处理方式。

总的来说,map 方法代表着一种将流中的元素进行处理,并将处理结果作为新的流的元素的操作。

3.可以用map代替foreach吗

般来说,map 方法不能代替 foreach 方法,因为它们的目的和实现方式不同。

foreach 方法是一种遍历操作,它可以对流中的每一个元素执行某个操作,但是不会返回任何结果,它只是简单地对数据进行处理。

而 map 方法是一种映射操作,它可以对流中的每一个元素执行某个操作,并将操作的结果作为新的流的元素。

因此,如果你需要遍历流中的元素,执行某个操作,但是不需要返回任何结果,那么应该使用 foreach 方法。如果你需要对流中的元素进行处理,并得到一个新的流,那么应该使用 map 方法。

二、金额计算

1. 在 Java 中,涉及到金额计算时,应该使用 BigDecimal 类型来确保精度和正确性。

BigDecimal 类型是一种高精度的数字类型,可以处理任意精度的十进制数,并提供了许多算术运算方法,例如加、减、乘、除、求余等等。与其他数字类型不同,BigDecimal 类型能够处理小数点后的任意位数,从而避免了浮点数计算时可能出现的舍入误差和精度丢失问题。

在处理金额时,由于金额通常需要精确到小数点后两位,因此使用 BigDecimal 类型可以保证计算结果的精度和正确性。同时,需要注意在创建 BigDecimal 对象时,应该使用字符串类型的构造方法,避免使用 double 或 float 类型转换而导致精度丢失。

List<PaymentInfoVO> list = ...; // 假设这是一个 PaymentInfoVO 类型的列表

BigDecimal totalAmount = list.stream()
                             .map(paymentInfoVO -> new BigDecimal(paymentInfoVO.getAmount()))
                             .reduce(BigDecimal.ZERO, BigDecimal::add);

System.out.println(totalAmount); // 输出 paymentInfoVO.getAmount() 的总和

在这个代码段中,我们使用 .map() 方法将每个 paymentInfoVO.getAmount() 的值转换为一个 BigDecimal 类型的对象,然后使用 .reduce() 方法计算它们的总和。其中,.map(paymentInfoVO -> new BigDecimal(paymentInfoVO.getAmount())) 表示将每个 paymentInfoVO.getAmount() 的值转换为一个 BigDecimal 类型的对象,.reduce(BigDecimal.ZERO, BigDecimal::add) 表示将所有 BigDecimal 类型的对象相加得到它们的总和,并使用 BigDecimal 类型来确保计算精度和正确性。最后,我们将结果保存在 totalAmount 变量中,并输出它的值。

总之,在处理金额计算时,使用 BigDecimal 类型可以确保计算结果的精度和正确性。如果要在 Stream 流中计算金额的总和,可以使用 .map() 方法将每个金额值转换为 BigDecimal 类型的对象,然后使用 .reduce() 方法计算它们的总和,并使用 BigDecimal 类型来确保计算精度和正确性。

2.BigDecimal.ZERO是什么

BigDecimal.ZERO 是 BigDecimal 类的一个静态常量,表示一个值为零的 BigDecimal 对象。

在使用 BigDecimal 类进行数值计算时,有时需要初始化一个初始值为零的 BigDecimal 对象,这时可以使用 BigDecimal.ZERO 来创建这个对象。例如:

BigDecimal number = BigDecimal.ZERO;

在这个例子中,我们使用 BigDecimal.ZERO 创建了一个值为零的 BigDecimal 对象,并将其赋值给 number 变量。由于 BigDecimal 类是不可变的,因此在对它进行运算或修改时,会返回一个新的 BigDecimal 对象,不会修改原始的 BigDecimal 对象。

因此,使用 BigDecimal.ZERO 可以方便地创建一个初始值为零的 BigDecimal 对象,并在 BigDecimal 类的计算中使用它。同时,由于 BigDecimal 类是高精度的十进制数类型,因此在处理数值计算时应该使用 BigDecimal 类型,并避免使用浮点数类型来确保计算结果的精度和正确性。

3.另外还有BigDecimal.ONE
是 BigDecimal 类的一个静态常量,表示一个值为 1 的 BigDecimal 对象。

在使用 BigDecimal 类进行数值计算时,有时需要初始化一个值为 1 的 BigDecimal 对象,这时可以使用 .ONE 来创建这个对象。例如:

BigDecimal number = BigDecimal.ONE;
4.乘法运算

在 Java 中,可以使用 BigDecimal 类型的 multiply() 方法来进行乘法运算。该方法接受一个 BigDecimal 类型的参数,表示要乘以的值,并返回一个新的 BigDecimal 对象,表示乘法运算的结果。

以下是一个示例代码,演示如何使用 BigDecimal 类型进行乘法运算:

BigDecimal num1 = new BigDecimal("12.34"); // 创建一个 BigDecimal 对象,表示第一个数值
BigDecimal num2 = new BigDecimal("5.67"); // 创建一个 BigDecimal 对象,表示第二个数值

BigDecimal result = num1.multiply(num2); // 使用 multiply() 方法进行乘法运算

System.out.println(result); // 输出乘法运算的结果

5.加减法

要进行加法和减法运算,可以使用BigDecimal类提供的add()和subtract()方法。

例如,如果要将availableAmount和settlementAmount相加,可以使用以下代码:

BigDecimal newAvailableAmount = availableAmount.add(settlementAmount);

如果要将willpayAmount和settlementAmount相减,可以使用以下代码:

BigDecimal newWillpayAmount = willpayAmount.subtract(settlementAmount);

在使用BigDecimal进行加减法运算时,需要注意以下几点:

  1. 必须使用BigDecimal的add()和subtract()方法进行运算,不要直接使用加减号。
  2. 进行运算的两个数必须都是BigDecimal类型的。
  3. 进行运算后会返回一个新的BigDecimal对象,原对象不会发生变化,因此需要将运算结果保存到一个新的BigDecimal对象中。
6.比较方法compareTo()

compareTo()方法是Java中Comparable接口的一个方法,用于比较两个对象的大小。在BigDecimal类中,这个方法用于比较两个BigDecimal对象的数值大小。

compareTo()方法的返回值如下:

  • 当调用对象的数值小于传入参数对象的数值时,返回值为负数(通常是-1)。
  • 当调用对象的数值等于传入参数对象的数值时,返回值为0。
  • 当调用对象的数值大于传入参数对象的数值时,返回值为正数(通常是1)。

以下是一个使用compareTo()方法的简单示例:

import java.math.BigDecimal;

public class BigDecimalCompareExample {
    public static void main(String[] args) {
        BigDecimal number1 = new BigDecimal("10.5");
        BigDecimal number2 = new BigDecimal("20.5");

        int comparisonResult = number1.compareTo(number2);

        if (comparisonResult < 0) {
            System.out.println("number1小于number2");
        } else if (comparisonResult == 0) {
            System.out.println("number1等于number2");
        } else {
            System.out.println("number1大于number2");
        }
    }
}

在这个示例中,我们创建了两个BigDecimal对象number1number2,然后使用compareTo()方法对它们进行比较。根据返回值,我们可以确定它们之间的大小关系。

三、lambdaQueryWrapper语句

1.save和insert方法的区别

在 MyBatis-Plus 中,saveinsert 方法都可以用来向数据库中插入一条新记录,但它们在使用上有以下几个区别:

  1. 参数类型不同:save 方法的参数类型是实体对象(Entity),而 insert 方法的参数类型是 Wrapper 对象或者实体对象(Entity)。
  2. 返回值不同:save 方法的返回值是一个 boolean 类型,表示插入操作是否成功,而 insert 方法的返回值是受影响的行数。
  3. 执行逻辑不同:save 方法会根据实体对象的主键属性是否有值来判断是插入还是更新操作,如果主键属性有值,就执行更新操作,否则执行插入操作;而 insert 方法只执行插入操作,不进行主键冲突检测。

因此,如果希望在插入新记录的同时进行主键冲突检测,可以使用 save 方法;如果只是简单地插入新记录,可以使用 insert 方法。

2.insert,add和save有什么区别

insert,add和save都是数据库操作中常用的用于插入数据的方法,不同的是在不同的数据库操作框架或者实现中可能有些许差异。

在一些ORM框架中,如Hibernate和MyBatis,它们的含义和用法如下:

  1. insert:insert是Hibernate和MyBatis中用于向数据库中插入数据的方法,用于将一个新的对象保存到数据库中。当使用Hibernate时,insert操作通常是通过Session的save()方法实现的;当使用MyBatis时,insert操作通常是通过Mapper接口的insert()方法实现的。
  2. add:add是Hibernate中用于向数据库中插入数据的方法,与insert类似,都是用于将一个新的对象保存到数据库中。不过,在Hibernate中,add操作通常是通过Session的persist()方法实现的。
  3. save:save是Hibernate中用于向数据库中插入数据的方法,与insert和add类似,都是用于将一个新的对象保存到数据库中。不过,在Hibernate中,save操作通常是通过Session的save()方法实现的,与insert的区别在于,save操作可以用于保存新的对象,也可以用于更新已经存在的对象。

需要注意的是,具体的操作方式和用法可能会因为具体的ORM框架或者实现而有所差异,因此在使用这些方法时,应该根据具体的情况进行使用和选择。

对于insert、add和save这三个方法,哪个更好需要根据具体的使用情况和需求进行选择。

一般来说,如果只是简单的插入一条新的记录,那么使用insert或add都可以,它们的实现方式和性能差异不大。但如果需要同时插入多条记录,或者需要支持一些高级的插入操作,那么就需要根据具体情况选择更为适合的方法。

在使用ORM框架时,一般建议使用ORM框架提供的方法进行操作,以便能够充分利用框架的特性和优化。但在某些特殊情况下,也可以考虑使用原生SQL语句进行操作,以获得更好的性能和控制力。

综上所述,需要根据具体的使用情况和需求进行选择,选择更为适合的方法。

3.除了以上三种方法外的其他插入方法

除了insert、add和save方法,还有其他一些更为适合的方法可以用于向数据库中插入数据,如:

  1. batch insert:批量插入是一种常见的向数据库中插入大量数据的方法,它通常比逐条插入数据要快得多。在使用ORM框架时,一般都会提供批量插入的支持,如Hibernate的batch插入。
  2. JDBC批量插入:使用JDBC进行批量插入是一种比较底层的方法,但是在插入大量数据时,性能通常会更好。在使用JDBC进行批量插入时,需要使用PreparedStatement和addBatch()方法。
  3. 使用存储过程:在一些特殊情况下,可以考虑使用存储过程来向数据库中插入数据。存储过程可以将多条SQL语句封装成一个过程,并在数据库中预编译和缓存,从而提高性能。

需要根据具体的使用情况和需求选择更为适合的方法,以获得更好的性能和控制力。

4.lambdaQueryWrapper中.eq和.in的区别

.eq方法是用于创建一个相等条件的查询,例如:

new LambdaQueryWrapper<User>().eq(User::getName, "John");

上面的代码表示在User表中查询所有名字等于"John"的记录。

.in方法则用于创建一个包含条件的查询,例如:

new LambdaQueryWrapper<User>().in(User::getId, Arrays.asList(1, 2, 3));

上面的代码表示在User表中查询所有id为1、2或3的记录。

因此,.eq用于创建单个条件,而.in用于创建多个条件。

五、方法
  • eq:等于
  • ne:不等于
  • gt:大于
  • ge:大于等于
  • lt:小于
  • le:小于等于
  • between:区间
  • notBetween:不在区间
  • like:模糊匹配
  • notLike:不模糊匹配
  • likeLeft:左模糊匹配
  • likeRight:右模糊匹配
  • isNull:为空
  • isNotNull:不为空
  • in:在集合中
  • notIn:不在集合中

如果你想通过姓名和身份证获取返回值,但是姓名和身份证不在一张表怎么办,你可能需要使用LambdaQueryWrapper的join方法来连接两张表,然后使用eq方法来指定查询条件。例如:

LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();

wrapper.join(User::getId, Staff::getUserId)// 连接User表和Staff表

.eq(User::getName, “张三”) // 查询姓名为张三

.eq(Staff::getIdCard, “123456789”); // 查询身份证为123456789

List list = userService.list(wrapper); // 获取返回值

.join方法是LambdaQueryWrapper中用于实现多表联查的方法,它可以指定要连接的表和连接条件,例如:

  • wrapper.join(User::getDeptId, Dept::getId)表示连接User表和Dept表,条件是User.dept_id = Dept.id。
  • wrapper.leftJoin(User::getDeptId, Dept::getId)表示左连接User表和Dept表,条件是User.dept_id = Dept.id。
  • wrapper.rightJoin(User::getDeptId, Dept::getId)表示右连接User表和Dept表,条件是User.dept_id = Dept.id。

使用.join方法时,需要注意以下几点:

  • .join方法只能用在自定义的mapper接口中,不能用在mybatis-plus提供的通用mapper接口中。
  • .join方法需要配合.select方法来指定查询的字段,否则会报错。
  • .join方法不支持嵌套查询或子查询,如果需要复杂的多表联查,请使用xml文件或注解方式。
5…apply方法
qwPayOrder
                .ne( "rec_state" , 200 )  //  排除 rec_state 为 200 的 订单 这边加入了对时间的限制
                .in("state", 60, 100) // 筛选 60 状态 和 100 状态 的 订单
                .apply( "YEAR(update_time) = {0} AND MONTH(update_time) = {1}", year, month )
                .select(
                        "IFNULL(SUM(CASE WHEN state = 100 THEN total_amount ELSE 0 END) + SUM(CASE WHEN state = 60 THEN total_amount - settled_amount ELSE 0 END), 0) as totalAmount",
                        "IFNULL(SUM(CASE WHEN state = 100 THEN total_service_amount ELSE 0 END) + SUM(CASE WHEN state = 60 THEN total_service_amount - settled_service_amount ELSE 0 END), 0) as totalServiceAmount",
                        "company_id"
                )
                .groupBy("company_id");

在这段代码中,apply 方法的作用是将传入的字符串参数应用到查询条件中。这里传入了一个字符串格式化表达式,其中 {0}{1} 分别表示 yearmonth 参数,用来限定 update_time 的年份和月份。在实际查询时,yearmonth 会替换 {0}{1},从而构成查询条件。例如,如果 year 为 2023,month 为 4,则查询条件为 YEAR(update_time) = 2023 AND MONTH(update_time) = 4。这样就实现了对 update_time 时间的限制。

四、数据库

1.索引

数据库索引是一种数据结构,用于快速查找数据库表中的数据。它是数据库管理系统(DBMS)用来优化查询性能的重要工具之一。通过在数据库表的一个或多个列上创建索引,可以显著提高查询效率,特别是在数据量较大时。索引可以是唯一的,也可以不是唯一的,取决于在创建索引时是否强制唯一性。在查询时,DBMS使用索引来定位需要的数据行,而不是扫描整个表,从而提高了查询效率。然而,索引也会占用额外的存储空间,并增加数据修改的成本,因此需要权衡索引的使用。

Java开发中有用的笔记_第1张图片

这是三个数据库表的索引信息:

  1. employer_id company_id ASC NORMAL BTREE:表示在该表中建立了一个名为 “employer_id” 的索引,该索引按照 “company_id” 字段的升序进行排序,索引类型是 BTREE。(BTREE 是一种数据库索引类型,它是一种平衡树结构的实现,用于加快数据库表中数据的查询速度。索引是一种数据结构,能够在进行查询时快速地定位到满足特定条件的数据。BTREE 索引使用一种基于二分查找的算法,将数据按照一定的规则保存在其中,并使用平衡树的思想减少搜索树的深度,快速找到需要的数据。这种索引类型通常用于数据量较大的表中,因为它可以显著地提高查询效率,并且对于定期更新的表也具有较高的效率。)
  2. bank_main_account_id bank_main_account_id ASC NORMAL BTREE:表示在该表中建立了一个名为 “bank_main_account_id” 的索引,该索引按照 “bank_main_account_id” 字段的升序进行排序,索引类型是 BTREE。
  3. public_id public_id ASC NORMAL BTREE:表示在该表中建立了一个名为 “public_id” 的索引,该索引按照 “public_id” 字段的升序进行排序,索引类型是 BTREE。

索引是数据库中用于快速查找和排序数据的一种数据结构。它可以提高数据检索的速度和效率,尤其是当数据量很大时。

2.ACS

ACS是索引的一种类型,表示使用了“自适应哈希索引”(Adaptive Hash Indexing)的优化技术。在数据库中,ACS索引类型通常用于高并发的查询操作,能够显著提高查询效率。

在MySQL中,索引默认是升序的,所以这里的ASC可以省略不写。如果要定义为降序,则需要写DESC。

3.外键约束

image-20230327164020824

这是一个数据库外键约束,约束名为 bank_main_account_ibfk_1,表示在 bank_main_account 表中有一个外键 park_id,它引用了 park 表的主键 id。约束条件为 RESTRICT,表示当 park 表中被引用的主键记录被删除或更新时,会阻止在 bank_main_account 表中的外键记录被删除或更新。具体来说,当试图删除或更新 park 表中的一个主键记录时,如果在 bank_main_account 表中存在引用该主键记录的外键记录,则该操作将被拒绝并抛出一个异常。

4.@Transactional注解
  1. 在类上添加@Transactional注解会默认为此类中的所有public方法开启事务
  2. @Transactional注解只对public访问级别的方法有效,对于private、protected和默认访问级别的方法是不起作用的
  3. @Transactional注解的工作原理是在方法执行前开启一个事务,在方法执行结束后提交或回滚事务。因此,只有在被外部调用的public方法上使用@Transactional注解才有意义。而private方法通常只被类内部的其他方法调用,不会被外部调用,所以在private方法上使用@Transactional注解也没有意义。
  4. 如果在private方法中需要开启事务,可以考虑将该方法的逻辑提取到public方法中,然后在该public方法上添加@Transactional注解,以确保事务的一致性和完整性。
  5. 如果在public方法上添加注解,方法中调用了一个private方法,那么这个private方法不会开启事务

注意:当一个有 @Transactional 注解的 public 方法调用一个 private 方法时,private 方法会继承 public 方法的事务,它们属于同一个事务。也就是说,private 方法不会单独开启新的事务,但它会成为调用它的 public 方法事务的一部分。

5.事务
  1. 在Spring框架中,事务指的是一组数据库操作,这些操作要么全部执行成功,要么全部执行失败,确保了数据库操作的一致性和完整性。

    具体来说,事务通常涉及到数据库的增、删、改等操作,这些操作需要在同一个事务中进行,以保证数据的一致性。如果一个事务中的任何一次数据库操作失败,整个事务都会被回滚,所有已经执行的操作都会被撤销,数据库恢复到事务执行前的状态。

    因此,在需要对数据库进行多个操作的情况下,使用事务可以确保操作的原子性,以避免数据不一致的问题。而Spring框架提供的事务管理功能可以简化事务的处理,降低了代码的复杂度,提高了开发效率。

6.索引的唯一键

Java开发中有用的笔记_第2张图片

  • "company_id"和"outer_order_no"是索引字段;
  • "ASC"表示索引按升序排列;
  • "UNIQUE"表示该索引是唯一索引,即"company_id"和"outer_order_no"组合在一起的值必须是唯一的;
  • "BTREE"是索引类型,表示使用B-tree索引方法。
7.索引

在数据库中,通常有四种类型的索引,包括唯一索引、主键索引、Normal索引和全文索引。这些索引的作用如下:

  1. 唯一索引(UNIQUE):保证索引列中的值唯一。当插入或更新数据时,数据库会检查唯一索引是否存在相同的值,如果存在,则拒绝插入或更新操作。唯一索引可用于确保数据的完整性和减少重复数据。
  2. 主键索引:保证主键列中的值唯一。与唯一索引类似,但主键索引是一种特殊的唯一索引,用于标识表格中每一行数据的唯一性。主键索引可以提高查询效率,并且在许多数据库中是必需的。
  3. Normal索引:基于B-Tree数据结构实现,可用于加速等值查询和范围查询。Normal索引是最常用的索引类型之一,可以提高查询速度。
  4. 全文索引(FULLTEXT):用于在文本数据中进行关键字搜索。全文索引可用于加速文本搜索操作,例如在文章、博客等大量文本数据中搜索关键词。

综上所述,不同类型的索引都有其独特的作用和用途,您可以根据具体的数据需求选择适当的索引类型来提高数据库的性能和查询效率。

  1. 唯一索引(Unique Index):确保索引列中的值唯一,用于保证数据的完整性和减少重复数据。
  2. 主键索引(Primary Key Index):保证主键列中的值唯一,用于标识表格中每一行数据的唯一性。主键索引可以提高查询效率,并且在许多数据库中是必需的。
  3. Normal索引(B-Tree Index):基于B-Tree数据结构实现,可用于加速等值查询和范围查询。Normal索引是最常用的索引类型之一,可以提高查询速度。
  4. 全文索引(Full-text Index):用于在文本数据中进行关键字搜索。全文索引可用于加速文本搜索操作,例如在文章、博客等大量文本数据中搜索关键词。
8.多表联查以及数据库优化

多表联查确实可能对性能产生影响,特别是当表中数据量很大时。这里有一些方法可以帮助避免或优化多表联查的性能问题:

  1. 选择性地查询所需列:尽量只查询所需的列,而不是使用 SELECT * 从每个表中获取所有列。

  2. 使用索引:确保在查询中涉及的所有表上都创建了合适的索引。这可以帮助数据库快速查找需要的数据,从而提高查询性能。

  3. 使用 INNER JOIN 代替 OUTER JOIN:在可能的情况下,尽量使用 INNER JOIN 代替 OUTER JOIN。INNER JOIN 通常比 OUTER JOIN 更快,因为它只返回匹配的行。

  4. 减少表的连接数:在可能的情况下,尝试减少查询中涉及的表的数量。例如,如果可以通过子查询或临时表的方式减少表的连接数,这可能有助于提高查询性能。

  5. 优化 WHERE 子句:对 WHERE 子句进行优化,例如使用 AND 而不是 OR,避免使用 NOT IN,使用 EXISTS 或 NOT EXISTS 代替。

  6. 优化查询顺序:尝试调整查询中的表连接顺序,以便先过滤掉更多的无关数据。这样可以减少后续连接操作的计算量。

  7. 分页查询:如果查询结果集很大,可以考虑使用 LIMIT 和 OFFSET 子句进行分页查询,每次只返回一部分数据。这样可以减轻数据库负担并提高用户体验。

  8. 使用数据库优化器的建议:大多数数据库系统都有查询优化器,可以分析查询并提供优化建议。根据优化器的建议调整查询,以提高查询性能。

  9. 将计算移到应用程序中:在某些情况下,可以考虑将一部分计算从数据库查询中移出,将其移到应用程序代码中。这可以减轻数据库的负担,但可能会增加应用程序的复杂性。

  10. 物化视图:对于经常需要执行的复杂查询,可以考虑使用物化视图。物化视图是预先计算好的查询结果集,可以大大提高查询性能。但请注意,物化视图需要定期更新以保持数据的实时性。

请注意,优化查询性能是一个复杂的过程,可能需要根据具体的查询和数据库系统进行调整。在进行优化时,请务必确保查询的正确性和数据的一致性。

五、TypeReference类

1.用法

在Java中,如果需要将JSON字符串转换成Java对象,通常可以使用ObjectMapper类来实现。ObjectMapper类是Jackson库中的一个核心类,可以将JSON字符串转换成Java对象,并将Java对象转换成JSON字符串。

在转换JSON字符串时,通常需要使用Java的泛型来指定转换后的数据类型。但是,如果只是简单地使用泛型,可能会出现类型擦除的问题,导致转换失败。为了避免这种问题,可以使用TypeReference类来获取map中的数据类型,确保转换的正确性。

TypeReference类是Jackson库中的一个工具类,可以用来获取泛型类型的具体类型。例如,在将JSON字符串转换成Map类型时,可以使用以下代码:

ObjectMapper mapper = new ObjectMapper();
String jsonStr = "{\"key\":\"value\"}";

Map<String, Object> map = mapper.readValue(jsonStr, new TypeReference<Map<String, Object>>() {});

在这个示例中,使用TypeReference类来获取Map类型的具体类型,然后将JSON字符串转换成该类型的对象。这样可以确保转换的正确性,避免类型擦除的问题。

在上面的示例中,也使用了TypeReference类来获取map中的数据类型,具体代码如下:

Map<String, Object> map = mapper.readValue(requestAckData, new TypeReference<Map<String, Object>>() {});

在将JSON字符串转换成Map类型的对象时,使用了TypeReference类来获取该类型的具体类型,避免了类型擦除的问题,保证了转换的正确性。

因此,TypeReference类可以用来获取泛型类型的具体类型,确保转换的正确性,避免类型擦除的问题。

六、在pom文件里面添加个人profile

1.在pom.xml文件中添加个人profile,可以通过以下步骤完成:
  1. 在pom.xml文件中添加一个标签,用于定义不同的profile配置。
<profiles>
  <profile>
    <!-- 个人profile的id -->
    <id>myprofile</id>
    <!-- 个人profile的配置 -->
    <properties>
      <my.property>myvalue</my.property>
    </properties>
  </profile>
</profiles>

  1. 在标签下的标签中添加标签,并指定个人profile的id。
<build>
  <plugins>
    <plugin>
      <groupId>com.example</groupId>
      <artifactId>my-plugin</artifactId>
      <version>1.0</version>
      <!-- 指定个人profile的id -->
      <profiles>
        <profile>myprofile</profile>
      </profiles>
    </plugin>
  </plugins>
</build>

  1. 在命令行中执行maven命令,指定使用个人profile的id来编译项目。
mvn clean install -Pmyprofile

七、关于Http请求

 //默认头信息,创建自定义的httpclient对象
        List<Header> defaultHeaders=new ArrayList<Header>();
        defaultHeaders.add(new BasicHeader("Accept","text/html,application/xhtml+xml,application/xml,application/json;q=0.9,*/*;q=0.8"));
        defaultHeaders.add(new BasicHeader("Accept-Language","zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"));
        defaultHeaders.add(new BasicHeader("Connection","close"));
        defaultHeaders.add(new BasicHeader("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0"));
        defaultHeaders.add(new BasicHeader("Authorization", ""));

image-20230328154036978

这段代码定义了HTTP请求的默认头部信息,具体含义如下:

  • “Accept”: 表示客户端能够接收的响应类型,优先级从高到低依次是"test/html"、“application/xhtml+xml”、“application/xml”、“application/json”,最后是"/"。
  • “Accept-Language”: 表示客户端能够接受的语言类型,优先级从高到低依次是"zh-CN"、“zh”、“zh-TW”、“zh-HK”、“en-US”、“en”。
  • “Connection”: 表示请求完成后是否立即断开连接,这里设为"close"表示立即断开连接。
  • “User-Agent”: 表示客户端的身份信息,这里设为"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0",表示使用的是Firefox 59.0浏览器在Windows 10系统上。

八、String

1.为什么用append代替+=

在Java中,字符串是不可变的,也就是说,一旦字符串被创建,就不能再修改它。因此,如果需要对一个字符串进行拼接,通常会使用字符串拼接符“+”,将多个字符串拼接在一起。

但是,在循环中多次使用字符串拼接符“+”会导致性能问题,因为每次使用“+”时,都会创建一个新的字符串对象。如果需要对大量字符串进行拼接,会导致大量的对象创建和垃圾回收,从而影响程序的性能。

为了解决这个问题,可以使用Java中的StringBuilder或StringBuffer类。这两个类都是可变的字符串类,可以在不创建新对象的情况下对字符串进行修改和拼接。在循环中使用StringBuilder或StringBuffer类的append()方法可以显著提高程序的性能,因为它们只会创建一个StringBuilder或StringBuffer对象,并在每次迭代中修改该对象。

因此,使用StringBuilder或StringBuffer类的append()方法代替“+”符号可以提高程序的性能,特别是在需要对大量字符串进行拼接时。

九、项目

1.代发摘要

代发摘要是指在支付代发业务中,支付机构向代发企业发放的一份代表该企业本次代发工资的汇总信息,也称为“工资代发凭证”或“代发工资明细单”。

代发摘要通常包括以下信息:

  1. 代发企业名称和代发日期
  2. 员工姓名和身份证号码
  3. 工资发放金额及税前、税后工资明细
  4. 代扣项目,如社保、公积金等
  5. 实际发放金额及发放日期

代发摘要是代发企业和员工获取代发工资信息的重要渠道,同时也是代发支付机构进行账务核对的重要依据。代发企业和员工可通过代发摘要了解自己的工资发放情况,同时也可以及时发现和纠正发放错误,确保代发工资的准确性和及时性。

十、并发问题

1.如果有多个线程同时访问同一段代码块,可能会出现以下几种并发问题:
  1. 资源竞争:多个线程同时竞争同一资源(例如共享变量、文件、网络连接等),会导致数据不一致或程序出错。
  2. 死锁:多个线程互相等待对方释放锁的情况,导致程序无法继续执行。
  3. 饥饿:某些线程因为资源分配不足而无法继续执行,导致程序无法完成任务。
  4. 并发访问异常:多个线程同时访问同一数据结构,可能会导致数据结构出现异常,例如数组越界、链表死循环等。

以上并发问题都可能导致程序出错、运行缓慢或无法完成任务,因此在编写多线程程序时需要特别注意并发问题的处理。常见的处理方法包括使用锁机制、同步机制、线程池、消息队列等。

2.在Java中处理并发问题时,可以使用多种方法。这里列举了几种常见的处理方法,你可以根据自己的具体场景选择合适的方案。
  1. 使用线程池

线程池是一种创建和管理线程的机制,可以有效地控制并发线程的数量。使用线程池可以避免过多的线程创建导致的性能问题,同时可以降低资源消耗。

例如,你可以使用Java标准库中的ExecutorServiceExecutors类来创建线程池:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        int poolSize = 10;
        ExecutorService executorService = Executors.newFixedThreadPool(poolSize);
        
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task());
        }
        
        executorService.shutdown();
    }
    
    static class Task implements Runnable {
        @Override
        public void run() {
            // 调用第三方接口
        }
    }
}
  1. 使用同步代码块或同步方法

为了避免线程间的竞争,可以在调用第三方接口的关键部分添加同步代码块或同步方法。这可以确保每次只有一个线程能够访问共享资源。

同步代码块示例:

public class SynchronizedExample {
    private final Object lock = new Object();

    public void callApi() {
        synchronized (lock) {
            // 调用第三方接口
        }
    }
}

同步方法示例:

javaCopy codepublic class SynchronizedMethodExample {

    public synchronized void callApi() {
        // 调用第三方接口
    }
}
  1. 使用信号量 (Semaphore)

信号量是一种同步工具,可以限制同时访问共享资源的线程数量。在Java中,可以使用java.util.concurrent.Semaphore类实现信号量。

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    private final Semaphore semaphore = new Semaphore(10);

    public void callApi() {
        try {
            semaphore.acquire();
            // 调用第三方接口
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }
    }
}

以上方法都可以在一定程度上解决并发问题。你可以根据你的具体需求和场景选择合适的方法。同时,在使用第三方接口时,需要遵循接口提供商的限制和规范,确保在规定的频率范围内调用接口。

3.相关代码
package com.crestv.lgpt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public Executor initExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(80);
        executor.setMaxPoolSize(100);
        executor.setQueueCapacity(80);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("ThreadPoolTaskExecutor- ");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        return executor;
    }

}

这个配置中的参数含义如下:

  • corePoolSize:线程池中的核心线程数,即线程池维护的最少线程数量。
  • maxPoolSize:线程池中的最大线程数,即线程池维护的最多线程数量。
  • queueCapacity:线程池中任务队列的容量,即可以等待执行的任务的数量。
  • keepAliveSeconds:非核心线程的空闲存活时间,即当线程池中的线程数量超过核心线程数时,多余的空闲线程的存活时间。
  • threadNamePrefix:线程池中线程的名称前缀。
  • rejectedExecutionHandler:线程池的拒绝策略,即当任务无法被线程池执行时的处理方式。这里使用的是CallerRunsPolicy,表示由调用线程来执行该任务。
  • waitForTasksToCompleteOnShutdown:是否等待所有任务执行完毕后再关闭线程池。
  • awaitTerminationSeconds:等待线程池关闭的最大时间。
  • executor.initialize():初始化线程池。

其中,corePoolSize就是线程池中的核心线程数,这里设置为80。当线程池中的任务数不超过80时,线程池中始终有80个线程在等待执行任务,即核心线程数不会变化。

十一、前端

1.写一个前端用于上传身份证
  1. 确保你已经安装了 Node.js 和 npm。你可以在这里下载和安装它们:https://nodejs.org/en/download/
  2. 使用 Vue CLI 创建一个新的 Vue.js 项目(如果你已经有一个现有的项目,可以跳过此步骤):
npm install -g @vue/cli
vue create my-project
cd my-project

替换 “my-project” 为你的项目名称。

创建好的项目如下:

Java开发中有用的笔记_第3张图片

  1. 将上述代码粘贴到一个新的 .vue 文件中,例如:IDCardUpload.vue。将此文件放入项目的 src/components 文件夹中。
  2. 在你的项目中安装 axios 库,用于发送 HTTP 请求:
npm install axios
  1. 在你的项目中的 src/main.js 文件中,引入 axios 并将其添加到 Vue 原型上:
import axios from 'axios';
import Vue from 'vue';

Vue.prototype.$axios = axios;

// 其他代码保持不变
  1. 在你的项目的 src/App.vue 文件中,引入并使用刚刚创建的 IDCardUpload 组件:
<template>
  <div id="app">
    <IDCardUpload />
  div>
template>

<script>
import IDCardUpload from './components/IDCardUpload.vue';

export default {
  components: {
    IDCardUpload,
  },
};
script>
  1. 在项目根目录下运行以下命令启动开发服务器:
npm run serve

现在你可以在浏览器中打开 http://localhost:8080,看到你的 Vue.js 项目正在运行,同时包含了刚刚创建的 IDCardUpload 组件。在这个组件中,你可以选择一个身份证图片并点击 “提交” 按钮上传至后端。

注意:请根据你的实际需求替换代码中的后端接口地址。

2.Vue.js 项目中的各个模块功能