跳至主要内容

博文

目前显示的是 十月, 2017的博文

Dev's Ranting - Map rendering in Freemarker

I’ve got a `Map ` and set it as model attribute in MVC Controller. Then I'd like to render this map in a loop in Freemarker view, e.g. ${key.prop} = ${myMap[key].fooProp} </#list> I have to loop through keys as they need to be displayed in a order. And surprisingly, it does NOT work!!! After searching dozens of stack overflow as well as Freemarker docs, I've learnt that: 1. If the map's key type is not `String`, you cannot utilise the sugar syntax like `map[key]`, actually you can but it would return null value... 2. It seems there is some way to re-configure the Freemarker to enable syntax like `map?api.get(key)`, but it’s not trivial. I tried a few changes but did not work (see the TLDR below). *TL;DR* 1. Need to set `api_builtin_enabled` to true, this can be done easily in our CustomFreemarkerConfiguration 2. After that it’s complaining "The value doesn't support ?api." and suggests to change another configuration on `object_wrapper` ...

Dev's Ranting - Collectors.toMap() in Java Stream

If you have a map and wants to convert into a different map, say mapping the value to something different, it would be sensible to utilise Java 8 Lambda, right? Map newMap = oldMap.entrySet() .stream() .collect(Map.Entry::getKey, entry -> someMapperFunc(entry.getValue())); Unfortunately the lambda does not ALWAYS Work, depends on whether there is any NULL value in the original map. As documented in JavaDoc, the intention of the `toMap()` is to "Returns a Collector that accumulates elements into a Map whose keys and values are the result of applying the provided mapping functions to the input elements." So why does it not want to accept null value? You'll have to be thorough and patient enough to read all the three overloaded versions of `toMap()` to be able to figure out that, `Collector.toMap()` relies on `Map.merge()`, which is designed to throw NPE when a NULL value is passed in. Who would expect that? Idiot library implementation ... Refer to: ...

Dev's Ranting - Hibernate Mapping Collections of Subtypes

Hibernate 作为ORM框架的中流砥柱被广泛地应用于企业应用程序开发中。从早期的XML配置到现在的Java Configuration,开发者早已习惯了使用各种 annotation 来配置 entity mapping。不过基础概念都是一样的。 为了展示用于一对多关系的多表映射,DZone上有一个[挺简单的例子](https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-one-to-many-using-annotations-1.html)。 @Entity @Table(name = "STUDENT") public class Student { private long studentId; private String studentName; private Set studentPhoneNumbers = new HashSet (0); @OneToMany(cascade = CascadeType.ALL) @JoinTable(name = "STUDENT_PHONE", joinColumns = { @JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "PHONE_ID") }) public Set getStudentPhoneNumbers() { return this.studentPhoneNumbers; } } 这两天遇到了一个定义映射关系的问题,与上面这个映射的区别在于,除了 Entity A 和 B 之间有 join table之外,Entity B 实际上是共享一个table的sub type,使用了 `@DiscriminatorColumn`...

阅读笔记:Git 源码 (0)

GitHub上检出代码,跳转到第一个commit,那是2005年4月提交的 `e83c5163316f89bfbde7d9ab23ca2e25604af290`。 需要认真复习我的C语言知识了,不过重点不是在于语言,而是这个工具本身的设计。 CACHE_H这个头文件就挺精彩,数据结构直接是基于硬件结构来设计的,注释写的很清楚“根本不考虑可移植性,因为这仅仅是个cache,一切以效率为优先考量”。 ``` /* * Basic data structures for the directory cache * * NOTE NOTE NOTE! This is all in the native CPU byte format. It's * not even trying to be portable. It's trying to be efficient. It's * just a cache, after all. */ #define CACHE_SIGNATURE 0x44495243 /* "DIRC" */ struct cache_header { unsigned int signature; unsigned int version; unsigned int entries; unsigned char sha1[20]; }; /* * The "cache_time" is just the low 32 bits of the * time. It doesn't matter if it overflows - we only * check it for equality in the 32 bits we save. */ struct cache_time { unsigned int sec; unsigned int nsec; }; ``` 此外,作为最基础的数据结构,CACHE_H也定义了一组接口函数。 ``` /* Initialize the cache information */ extern int read_cache(void); /* Return a statically all...

Dev's Ranting - OnCommittedResponseWrapper in Spring

一转眼已经到了十月,我的草稿箱里还存着一个八月遇到的诡异bug:在我们的Spring Boot应用程序里,用到了Spring Security和Spring Session,这两个modules一直以来相安无事,直到有一天…… 使用Hibernate的时候如果查询结果集合过大,会导致response无法迅速返回。这里有两个要命的问题,首先由于Query在执行的时候JDBC的block调用过长,HTTP Gateway会超时;其次结果集过大意味着Hibernate在真正写入response (例如将结果集转换为DTO再写入CSV)之前不得不在内存中持有所有结果的对象引用,最终会致内存占用的问题,要么GC要么OutOfMemory。 为了解决在查询结果下载时遇到的问题,我们用到了`StatelessSession`和`ScrollableResult`来获取一个相当大的`ResultSet`,通过Java 8 的`Stream` 来访问单个元素,从而实现stream downloading。但是在调试中发现无法得到完整的CSV结果,经常是写到半当中就EOF了?! 同事老R花了大半天的时间来debug,最终无比惊讶的发现是Spring在控制`HttpServletResponse`的committed事件时有隐藏手段——`OnCommittedResponseWrapper`。貌似这个class在 [Spring Session](https://github.com/spring-projects/spring-session/blob/master/spring-session-core/src/main/java/org/springframework/session/web/http/OnCommittedResponseWrapper.java) 和 [Spring Security](https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/util/OnCommittedResponseWrapper.java) 内各自有一份copy,而且还并非完全一样。 /** * Imp...