Total Pageviews

2018/02/11

[Liquibase] How to fix checksum validation error

Problem
I am editing changelog-master.yaml, I got error message as I start up Spring boot:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: 
Invocation of init method failed; nested exception is liquibase.exception.ValidationFailedException: Validation Failed:
     1 change sets check sum
          classpath:/db/changelog/db.changelog-master.yaml::1::albert was: 7:6e574692a29b35c6788f06c99fe2eecc but is now: 7:c5994a0c5ad9c2ba211a4f8e3ced2ec9


How-To
Here has the process to solve this problem:
1. drop all tables which had already created in liquibase
2. clear all data in databasechangelog table
delete from databasechangelog


2018/02/10

[Ant] java.lang.UnsupportedClassVersionError: com/sun/tools/javac/Main : Unsupported major.minor version 52.0

Problem
When I use Ant 1.10.1 to build my Java project, it occurs error as bellows:
java.lang.UnsupportedClassVersionError: org/apache/tools/ant/launch/Launcher : Unsupported major.minor version 52.0

How-To

The problem is 1.10.x releases require Java 8 at run time, but I am using Java 7 in my project.
Therefore, I need to use Ant 1.9.x releases instead of 1.10.x releases



2018/02/09

[Maven] How to stop Maven to check for updates for artifacts in offline mode ?

Problem
I am using offline mode to build my project, the command is as bellows:
mvn -o -Dmaven.repo.local="C:/Users/albert/.m2" clean install -Dmaven.test.skip=true

During the build process, Maven still check for updated for certain artifacts even in offline mode. How to avoid it?


How-To

Go to your local repository (ex. C:/Users/albert/.m2), and delete all *.lastupdated and _remote.repositories files.
Then Maven won't check updates any more.




2018/02/08

[PostgreSQL] How to assign customized numbering rule in primary key

Problem
Assume I have a table, loan_main, which have a primary key, loan_id, with numbering rule : LN_YYYYMMDD_9999999999.
How to insert data into loan_main automatically without caring how to build loan_id with this numbering rule.

How-to


In this case, we need to know
1. how to concatenate string, i.e. ||
2. how to get current date with specific format:
select to_char(now(), 'YYYYMMDD')

3. how to get value from sequence and pad zero to the left:

select LPAD(nextval('loan_sequence')::text, 10, '0')


The syntax looks like:
ALTER TABLE loan_main ALTER COLUMN loan_id SET DEFAULT ('LN_' || (to_char(now(), 'YYYYMMDD')) || '_' || LPAD(nextval('loan_sequence')::text, 10, '0'));





2018/02/07

Java Bean Validation example

Add dependencies to your pom.xml
    <!-- validation start  -->
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>2.0.0.Final</version>
    </dependency>

    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.0.2.Final</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b08</version>
    </dependency>
    <!-- validation end -->
        
    <!-- DI start -->
    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>4.1.0</version>
    </dependency>
    <!-- DI end -->
        
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>$1.16.18</version>
        <scope>provided</scope>
    </dependency>
    
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>21.0</version>
    </dependency>


Scenario
Assume I have a Customer bean, it have 6 attributes which include id, name, gender, age, email and dateOfBirth.
These attributes have its validation rules:

  1. id should not be null
  2. name should not by null or empty
  3. gender should not be null
  4. age should be between 18 and 60
  5. email should not be null or empty and should have valid format
  6. dateOfBirth should be before today


The Java bean with bean validation annotation looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package albert.practice.validation;

import java.util.Date;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;

import org.hibernate.validator.constraints.Range;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
public class Customer {
    public static enum Gender {
        MALE, FEMALE;
    }

    @NotNull(message = "顧客編號不可為 null")
    private Long id;

    @NotEmpty(message = "姓名不可為空")
    private String name;

    @NotNull(message = "性別不可為空")
    private Gender gender;

    @Range(min = 18, max = 60, message = "年齡必須介於 18 到 60 歲之間")
    private Integer age;

    @NotEmpty(message = "Email 不可為空")
    @Email(message = "Email 格式不合法")
    private String email;
    
    @Past(message = "生日必須要今天以前")
    private Date dateOfBirth;
}


Assume I have a CustomerService class to create Customer, it should validate Customer Java Bean before do insert. The main validate logic will be in validate method (line 25 ~ 38).
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package albert.practice.validation;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.inject.Singleton;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Singleton
public class CustomerService {

    public void create(Customer customer) {
        validate(customer);
    }

    public static void validate(Object obj) {
        List<String> errors = new ArrayList<>();

        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Object>> violations = validator.validate(obj);
        for (ConstraintViolation<Object> violation : violations) {
            errors.add(violation.getMessage());
        }

        String completeErrorMsg = Joiner.on("\n").join(errors);
        if (!Strings.isNullOrEmpty(completeErrorMsg)) {
            throw new IllegalArgumentException(completeErrorMsg);
        }
    }

}



The resource class will prepare Customer Object and invoke create method which provide by CustomerService
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package albert.practice.validation;

import com.google.inject.Guice;
import com.google.inject.Inject;

public class CustomerResource {

 @Inject
 private CustomerService service;
 
 public static void main(String[] args) {
    Customer customer = new Customer();
    customer.setId(null);
    customer.setAge(12);
    customer.setEmail("test@gmail.");
  
    new CustomerResource().createCustomer(customer);
 }
 
 public void createCustomer(Customer customer){
    service = Guice.createInjector().getInstance(CustomerService.class);
    service.create(customer);
 }
 
}


Execution message in console:
1
2
3
4
5
6
7
8
9
Exception in thread "main" java.lang.IllegalArgumentException: 性別不可為空
年齡必須介於 18  60 歲之間
Email 格式不合法
顧客編號不可為 null
姓名不可為空
 at albert.practice.validation.CustomerService.validate(CustomerService.java:35)
 at albert.practice.validation.CustomerService.create(CustomerService.java:20)
 at albert.practice.validation.CustomerResource.createCustomer(CustomerResource.java:22)
 at albert.practice.validation.CustomerResource.main(CustomerResource.java:17)



2018/02/06

[JSR 303 - Bean Validation] HV000183: Unable to initialize 'javax.el.ExpressionFactory'.

Problem
I am using JSR 303 to do bean validation.

When I try to initialize 
Validator instance ..... 
Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); 

But I get the following error message: 

Exception in thread "main" javax.validation.ValidationException: HV000183: Unable to initialize 'javax.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
 at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:115)
 at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:46)
 at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolator(ConfigurationImpl.java:420)
 at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(ConfigurationImpl.java:596)
 at org.hibernate.validator.internal.engine.ConfigurationImpl.getMessageInterpolator(ConfigurationImpl.java:355)
 at org.hibernate.validator.internal.engine.ValidatorFactoryImpl.<init>(ValidatorFactoryImpl.java:149)
 at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
 at org.hibernate.validator.internal.engine.ConfigurationImpl.buildValidatorFactory(ConfigurationImpl.java:322)
 at javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:103)
 at albert.practice.validation.CustomerService.validate(CustomerService.java:38)
 at albert.practice.validation.CustomerService.create(CustomerService.java:21)
 at albert.practice.validation.CustomerService.main(CustomerService.java:32)
Caused by: java.lang.NoClassDefFoundError: javax/el/ELManager
 at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:107)
 ... 11 more
Caused by: java.lang.ClassNotFoundException: javax.el.ELManager
 at java.net.URLClassLoader.findClass(Unknown Source)
 at java.lang.ClassLoader.loadClass(Unknown Source)
 at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
 at java.lang.ClassLoader.loadClass(Unknown Source)
 ... 12 more
    



How-to
Add this dependency to your pom.xml
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b08</version>
    </dependency>



Reference
[1] https://stackoverflow.com/questions/42718869/hibernate-validation-unable-to-initialize-javax-el-expressionfactory-error

2018/02/05

[Google Guice] Dependency Injection Example

Google Guice is the framework to automate the dependency injection in applications. Google Guice is one of the leading frameworks whose main work is to provide automatic implementation of dependency injection. 



How-To
Step 1. Add dependency in your pom.xml
1
2
3
4
5
    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>4.1.0</version>
    </dependency>


Step 2. Create a service class to retrieve a person's contact people
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package albert.practice.guice;

import java.util.List;

import com.google.common.collect.ImmutableList;
import com.google.inject.Singleton;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Singleton
public class ContactService {

    public List<Contact> getContacts(String id) {
        log.debug("person id is " + id);
        return ImmutableList.of(new Contact("Mandy", "0912111111"), new Contact("Dad", "0911111111"),
                new Contact("Mom", "0922222222"));
    }

}


Step 3. Create a resource to inject service class and retrieve person's contact people 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package albert.practice.guice;

import java.util.List;

import com.google.inject.Inject;


public class PersonResource {
 
    @Inject
    private ContactService contactService;
    
    public List<Contact> getContacts(String id) {
        return contactService.getContacts(id);
    }
    
}



Step 4. Create a test class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package albert.practice.guice;

import static org.junit.Assert.assertEquals;

import java.util.List;

import org.junit.Before;
import org.junit.Test;

import com.google.inject.Guice;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PersonResourceTest {
 
    private PersonResource personResouce;
    
    @Before
    public void setup() {
        personResouce = Guice.createInjector().getInstance(PersonResource.class);
    }
    
    @Test
    public void testGetContacts() {
        List<Contact> contacts = personResouce.getContacts("123");
        contacts.stream().forEach(c -> log.debug(c.toString()));
        
        assertEquals(3, contacts.size());
    }
}
    





2018/02/04

[閱讀筆記] The Little Book That Builds Wealth (1/7)


  1. 巴菲特曾說:偉大的企業必須有偉大的護城河,一種是低成本(如 GEICO 保險公司和Costco 超市),一種是大品牌(如可口可樂、Nike 和美國運通公司)
  2. 巴菲特把競爭優勢壁壘稱為“護城河”(moats):“那些所提供的產品或服務周圍具有很寬的護城河的企業能為投資者帶來滿意的回報。”“我們喜歡持有這樣的‘企業城堡’:有很寬的護城河,河里游滿了很多鯊魚和鱷魚,足以抵擋外來闖入者。
  3. 護城河的要義,就是讓事業成為這家公司的獨占,讓對手因法令、因專利、因成本、因地緣等種種關係而無法踏進,那這家公司便有了護城河。
  4. 護城河(moats)是 Morningstar 的選股基礎,其推薦長期投資人要挑選擁有廣泛經濟護城河的公司,長期來說,這類的公司可以獲得高於平均的報酬。你可以長期持有這類型的公司,降低交易成本,獲得較好的報酬。wide-moat company 應是每個人的核心投資組合
  5. 投資股票的策略:
    1. 找出未來幾年可以產生高於平均獲利的公司
    2. 耐心等待到公司股價低於內在價值後再買進
    3. 長期持有股票直到公司營運出現衰退、股價高估或找到更好的投資標的。持有的期間通常是幾年,不會是幾個月
    4. 重複上述三步驟,如果需要的話
  6. 公司持久與否,在於其是否有強大的競爭優勢與經濟護城河,若無明顯競爭優勢與護城河,很容易從英雄變狗熊(from hero to zero)。擁有護城河的公司一定較有價值,也是你該投資的對象
  7. 擁有護城河的公司較值得購買,因為其會長期產生獲利。當你購買具有護城河的公司的股票,你就是買進一間未來幾年受到保護的公司。如同你用較高的價格買進一台好車可以讓你開個十年,若貪便宜買了一台爛車,可能幾年後就會讓你顧路
  8. 高獲利的市場必定會吸引眾多競爭者加入,若沒有護城河的保護,過去的輝煌馬上就成為歷史,這類型的公司稱為 hot-today-and-gone-tomorrow stocks
  9. 擁有護城河的公司會隨著時間穩健地增加公司的內在價值(intrinsic values),這對你的投資回報較有保障
  10. 擁有護城河的公司也擁有較好的回復力,當遇到暫時的困難時,可以利用其原先的競爭優勢恢復公司元氣
  11. 企業的價值在於其未來可以產生的所有現金
  12. 資本回報率(Return on capital)是用來判斷企業獲利能力的最佳方式。其可用來衡量企業如何運用投資人的錢與替投資人產生回報
  13. 資本報酬率是用以衡量公司運用所有資本所獲得經營成效的指標。比率越高,表明公司資本的利用效率越高,反之則資本未得到充分利用。在股票交易市場,股票公開上市的條件之一,就是公司在近幾個年度內資本報酬率必須達到一定的比率。例如,臺灣證券交易所規定,資本報酬率低於 6% 的公司,其股票不得上市;資本報酬率在 6%~8% 之間的公司,其股票只能被列為第二類上市股票;只有資本報酬率達到 8% 以上的公司,其股票才能准予列為第一類上市股票。
  14. 經濟護城河(economic moats)可以保護企業免於流血競爭,長期來說可幫助公司賺更多的錢,對投資人來說就是好的投資標的
  15. Bet on the Jockeys, Not the Horses (不賭馬,賭騎士)通常是指在評估潛在投資案時,創投家通常會查核公司創辦人,檢視他們的背景、評量他們先前的企業、打聽他們的風評。在 harvard business review 中,一項針對 681  家公司裡 885 個創投單位的新研究顯示,許多創投認為,最重要的因素是管理團隊,尤其是創辦人,這甚至比商業構想還重要
  16. 護城河是企業的結構性特色,此優勢可以持續數年且競爭者非常難以複製。但是有些是『誤認的護城河』,如『好產品』(great product)、『高市佔率』(high market share)、『好的執行力』(great execution)與『好的管理』(great management),這四個陷阱會讓你誤以為此公司擁有護城河
  17. 『好產品』很少能建構出護城河,儘管好產品可以產生短期榮景,如 Chrysler 在 1980 年代推出第一款小轎車,替公司印出大筆鈔票,如此成功的成績單,馬上受到 Chysler 相關競爭者的注意,隔年馬上推出相似的款式。由於沒有結構性的特色可以避免其他競爭者侵蝕 Chysler 的利潤,很快的榮景就不復存在
  18. 除非公司擁有經濟護城河來保護自己的業務,競爭者很快就會上門,吃掉你的利潤
  19. 沒有經濟護城河,一家公司的財務業績會急煞車(turn on a dime)
  20. 高市佔率 (high market share) 不代表已經挖好經濟護城河 (economic moats),如 Kodak (底片)、IBM (個人電腦)、Netscape (瀏覽器)、GM (汽車) 與 Corel (文字處理軟體) 等,這些企業在當時都擁有 NO.1 的市占率,但是由於沒有挖好護城河,市占率馬上被其他競爭者侵蝕殆盡。對於投資人來說,與其研究企業如何達到如此的市佔率,不如研究其如何做好防禦、如何捍衛目前的市占率
  21. 企業規模可以幫助公司建立競爭優勢,但是很少會變成經濟護城河,如同市占率也不是護城河的必要元素
  22. 好的執行力與營運效率並無法讓你獲得長久的競爭優勢,除非你是基於一些專屬的流程 (proprietary process) 且不易被複製
  23. 真正的經濟護城河 (economic moats) 是:
    1. 無形資產 (intangible assets):如品牌、專利 (patents) 或法規授權販售商品與服務 許可(其他競爭對手未獲法規許可就不可販售) 等
    2. 轉換成本 (switching costs):公司販售的商品或服務讓客戶很難放棄,因公司建立了昂貴或困難的客戶轉換成本
    3. 網絡效應 (network economics):公司幸運地從網路效應中獲利 (指的是愈多人使用,便愈有價值的服務,即大者恆大),這是一個非常強大的經濟護城河,可以將競爭者隔離在外
    4. 成本優勢 (cost advantage):公司由於其生產流程、地理位置、規模或獨特的資產,導致其擁有成本優勢,能夠提供價格低於競爭者的產品或服務
  24. 所謂的這些公司比那些公司好,是指這些公司有較大的可能性產生持久的、較高的回報
  25. 好的產品、巨大的規模、良好的執行力與管理無法建立長久的競爭優勢,這些是 nice to have,但是還不夠