《100 Java Mistakes and How to Avoid Them》笔记 3

本书的阅读又搁置了许久,虽然感觉 Manning 出版社的这一 100 Mistakes 系列从书的质量不是那么的高,但开了头还是继续从本书 40% 的位置往下。

开始要讲述到异常了,异常还是有必要认真对待的,比如

  1. Java 中很容易被 CheckedException 弄得代码不整洁
  2. 缺少必要的参数检查,不舍得抛出异常,视异常为 Bug
  3. 不明确出现异常时后续如何处理,
  4. 或者是捕获而隐藏了异常致使定位错误变得更难。

Java 的主要异常大分类是

Throwable
├── Error
└── Exception
          └── RuntimeException

NullPointerException, 这恐怕是一个最常见的异常,Java 对一个对象是否能为 null 值没什么约束,甚至用 null 来表示业务上的空。比如说方法的参数与返回值,Java 都可以是 null 值,而在 Kotlin 中非明确可为 null 的时不能为 null 阅读全文 >>

《100 Java Mistakes and How to Avoid Them》笔记 1

这几日在阅读 Manning 出版社的 《100 Java Mistakes and How to Avoid Them》, 其中列举的确实是一些容易带入到代码中的错误,不少还是通过代码 Review 或单元测试很难发现的问题。也有些看似很弱智,却可能是隐匿许久的定时炸弹,只等某一特定条件出现时即爆。

阅读的同时简单的作了笔记及少许联想,所以内容有些杂乱无条理。最前面介绍了一些静态代码分析工具,也有两个动态分析工具。本书目前还是 Manning 的 MEAP 体验版,未正式发售。一共讲了 100 个常见错误如何避免(例如,怎么用最新 Java(Java 9 -- Java 21) 语法, API 来改进),以及用静态分析工具,单元测试及早发现。

这是读完了 1/4 数量的记录,笔记开始 阅读全文 >>

postgres in (?,?) 和 =any(?) 用法/性能对比

刚刚回顾了一下 JDBC 操作 SQL Server 时如何传入列表参数,即如何给 in (?) 条件直接传入一个列表参数,然而本质上是不支持,最终不得不展开为 in (?, ?,...?) 针对每个元素单独设置参数,不定长的参数对于重用已编译 PreparedStatement 语句的帮助不大。

那么 JDBC 操作 PostgreSQL 是何种状态呢?展开为多个参数当然是有效的。继续尝试 Spring 提供的 NamedParameterJdbcTemplate 的操作方式

String query = "select * from users where id in (:ids)";
Map<String, Object> parameters = new HashMap<>();
parameters.put("ids", IntStream.rangeClosed(1, 5).boxed().collect(toList()));
List<Map<String, Object>> maps = namedParameterJdbcTemplate.queryForList(query, parameters);

执行后查看到实际执行的语句是

select * from users where id in (?, ?, ?, ?, ?)

阅读全文 >>

Spring Boot 如何选择 Cache 实现的

写作此篇是作为对 Spring 使用 Cache 解析及使用不同类型的 Cache 一文的补充,该文中提到了自定 CacheManager 及配置 spring.cache.type 来选择自己的 Cache 实例,但对 Spring 是如何确定具体 Cache 实现未作展开。本文将介绍选择 Cache 实现的几种方式

  1. 默认选择 Cache
  2. 声明 Spring Bean Cache
  3. 声明 Spring Bean CacheManager
  4. 通过 spring.cache.type 属性选择
  5. 引入相应的 Cache 实现依赖

阅读全文 >>

Spring 使用 Cache 解析及使用不同类型的 Cache

要在一个 Spring 应用中开启缓存方法返回结果的功能很简单,不需要额外的依赖,相关的的注解  @Cacheable, @CacheConfig, @CachePut, @CacheEvict, @EnableCache 等来自 spring-context 包。默认的的 Cache 实现是把数据存入到 ConcurrentMap 中,所以数据一直在内存中,除非显式的调用被 @CacheEvict 的方法来清理。实际进行数据缓存时会有更复杂的策略,如元素个数,占用内存,过期时间,何时使用磁盘等,而且不同的数据类型应有不同的缓存策略。

因此,除了使用默认的 ConcurrentMap 作为缓存外,还可通过配置属性 spring.cache.type 来使用其他类型的缓存,如 Caffeine, Couchbase, EhCache, INfinispan, JCache, Redis 等,或自定义 CacheManager 来使用 Guava Cache。 阅读全文 >>

理解 Spring Boot Security + JWT Token 的简单应用

项目中有用到 Spring Security 来控制 API 的访问权限,但对于配置应用它基本上是照葫芦画瓢。至于为什么要调用方法

SecurityContextHolder.getContext().setAuthentication()

并且能从 HttpServletRequest 中得到 Authentication。还有,只要在 Controller 的方法中添加一个带 @AuthenticationPrincipal 注解的参数

public String sampleApi(@AuthenticationPrincipal DecodedJWT decodedJWT) {...}

之后,decodedJWT 便自动有了值,诸如此类的,此前一概模糊不清。

早先配置 spring-security-config 是通过继承 WebSecurityConfigurerAdapter, 覆盖它的 configure(HttpSecurity http) 来配置访问规则等。在 spring-security-config 5.7.x 开始不建议用 WebSecurityConfigurerAdapter, 而是借由 SecurityFilterChain 来配置 HttpSecurity 中的规则 ,或者通过 WebSecurityCustomizer 完成定制。 阅读全文 >>

流畅的 Python 读书笔记(二)

继续啃这本略微有些旧的书,《Fluent Python》第二版出版在即,预计今年四月份,它将会讲解到更新版本 Python 的特性,书中有提到 Python 3.10。第一版读下来也不会是浪费时间的。

还是数据结构,现在来到字典,dict 是 Python 语言的基石,在它内部也被广泛应用,比如 type(globals()) 是个  dict, globals() 的内容有我们能调用的全局函数。如果在编程中不想创建新对象的话,dict 几乎能表述需要的数据结构。

dict 就是一个 hash 表,它是 collections.abc 下 Mapping 和 MutableMapping 的子类。和其他语言中的字典一样,dict 的 key 必须实现了 __hash__()__qe__() 方法,否则不能作为 key,比如 list 是不能作 key 的。两个key 的 hash() 相等的话,那么  key1 == key2, 反之不一定。所以当 __eq__ 依赖于可变状态就不要去实现 __hash__ 方法. 阅读全文 >>

流畅的 Python 读书笔记(一)

用了一段时间的 Python, 觉得还是有必要读一下《流畅的Python》这本书,它虽然是基于 Python 3.4 的,但 Python 自身的很多特性希望了解的更多,更深,或巩固,或扫扫死角。

对于少量属性的对象可以用 collections.namedtuple 快速构建一个类  Card = collections.namedtuple('Card', ['rank', 'suit']), 用 type(Card) 看到的就是一个  class, 第一个参数 Card 是类名,第二个参数列表里是属性名,然后用 card = Card('7', 'diamonds') 创建一个实例。PyCharm 也能正确识别出 Card 构建与使用对象时的属性 rank 和 suit.

现代从 Python 3.7 开始引入了 @dataclasses.dataclass 比 namedtuple 要方便些

@dataclasses.dataclass
class Card:
    rank: str
    suit: str = None

card = Card(rank='7', suit='diamonds')

或者用 pydantic 的 BaseModel 都比先前的 namedtuple 好用 阅读全文 >>

Java 直接插入 CLOB/BLOB 数据到 Oracle 数据库

向数据库中插入 CLOB 或 BLOB 类型的数据,Oracle 总是比其他类型的数据库操作上要麻烦多了。当然,对于不大于 4K 长度的 CLOB 字符串在 JDBC 中可简单的用 PreparedStatement.setString(idx, "short string") 。如果要插入大于 4K 长度的内容,网上找来的例子许多都是分两步走

  1. 先插入 EMPTY_CLOB() 或 EMPTY_BLOB()
  2. 然后 SELECT 原来的记录 FOR UPDATE, 再更新先前插入的记录

这就存在两个问题,含 CLOB/BLOB 的表必需要有主键,还有因为 FOR UPDATE 的使用我们需要开启事物,不能采用自动提交。

其实还有更简单的方法可直接插入大的 CLOB/BLOB 数据,要用到 Oracle JDBC 驱动的 setStringForClob(),  CLOB.createTemporary(), 或 BLOB.createTemporary() 方法。来看下面的例子,例子中只演示 CLOB, 类似的方法可应用于 BLOB, NCLOB。

本文中所使用的 Oracle JDBC 驱动比较老,是 ojdbc:ojdbc:5。Docker 启动一个本地的 Oracle 11G 作为测试数据库

$ docker run -d -p 1521:1521 -p 8080:8080 wnameless/oracle-xe-11g-r2

默认的 SID 是 xe, 数据库用户名和密码分别是 system/oracle 阅读全文 >>

Java 10 ~ 16 一路向前冲(新特性一箩筐)

Java 一路突突突, 版本 16 在 2021-03-16 都发布了, 而我们一直碍于 Java 9 的大改还在 Java 8 上原地踏步, 以往每当有新版本 JDK 发布后都是很快就验证,立马升级。Java SE versions history) 列出了所有 Java 的历史版本的发布日期。在今天(2021-05-04) 网站 Java SE Downloads 上直接提供下载的 Java SE 版本有以下三

  1. Java SE 16.0.1
  2. Java SE 11.0.11(LTS)
  3. Java SE 8u291

两个 LTS(长期支持) 版 8 和  11,外加一个目前最新的非  LTS 版本 16, 其他的版本都被归档到了 Java Archive. 查看一下 Java 支持的 roadmap, 几个 LTS 版本的服务支持年限

版本本      发布日       原定支持至      延期支持至
Java 8      2014/3       2022/3             2030/12
Java 11     2018/9      2023/9              2026/9
Java 17     2021/9      2026/9              2029/9

注意到 Java 8 将比 Java 11 和将来的 Java 17 生命力还顽强,一下就觉得这么久坚守在 Java 8 的阵地上不应觉得有什么好害羞的。眼看着下一个 LTS 版本的 Java 17 就要在今年 9 月份发布了,Java 11 看来是要错过了,等准备好和 Java 8 告别时要直接跳到 Java 17 了。 阅读全文 >>