2015年5月22日 星期五

Resolved : Set the Database Default Value in a Hibernate/JPA Save Persist

Using JPA (with Hibernate), I was inserting a record in my database. 
In my table, one column is defined with a DEFAULT Value.
But when I insert a save, persist my record, JPA put it as NULL.
So using JPA, the solution was to use insertable = false in the column definition. 
JPA will ignore then that column while inserting in the Database and the default value will be used.
@Column(name = "myColumn", insertable = false)

Spring AOP Around Advice 實作 Exception 處理

緣由:專案內需要將Exception獨立抽出來處理,為了不影響原有的商業邏輯,並且讓code reuse
所以第一時間就想到要用AOP實作,順便回去練一下很久沒碰的Spring AOP,並複習一下觀念
問題:Exception處理
解決方式:Spring AOP Around Advice
好處:
1. 統一錯誤訊息,讓每個工程師各寫各的錯誤處理風格可以得到統一。
2. 讓其他人可以專注在商務邏輯
3. 程式看起來比較簡潔乾淨
基本上我不解釋觀念,因為網路上已經有很多很棒的解說了
我也還在學習,很多東西其實第一次看完全都不懂,很抽象,
但隨著時間、經驗的累積,又回去看了第二次第三次,就會越來越有感覺
spring-aop-diagram.jpgspring-aop-diagram.jpg
In Spring AOP, 4 type of advices are supported :
  • Before advice – Run before the method execution
  • After returning advice – Run after the method returns a result
  • After throwing advice – Run after the method throws an exception
  • Around advice – Run around the method execution, combine all three advices above.
============================= 分隔線 =================================
我是使用 Around advice ,其實也就是把程式裡散落在各處的try catch集中至ExceptionAdvice統一處理。
build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'maven'

compileJava.options.encoding = 'UTF-8'
def springVersion = "3.2.6.RELEASE"

dependencies {
    compile 'org.aspectj:aspectjweaver:1.7.1'
    compile 'org.slf4j:jcl-over-slf4j:1.7.1'
    compile 'org.slf4j:slf4j-log4j12:1.7.1'
    compile "org.springframework:spring-aop:$springVersion"
    compile "org.springframework:spring-context:$springVersion"
}
applicationContext.xml


    

    
    
    
    
        
            
            
        
     

com.test.aop.advice.ExceptionAdvice
public class ExceptionAdvice {
    private static Logger log = LoggerFactory.getLogger(ExceptionAdvice.class);

    public Object exceptionHandler(ProceedingJoinPoint proceedingJoinPoint) {
        Object value = null;

        try {
            value = proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            log.info(proceedingJoinPoint.getSignature().getName() + " error");
            
            String errorMsg = "內部錯誤:" + e.getMessage();
            String errorCode = "500";
            
            Map resultJsonMap = new HashMap();
            resultJsonMap.put("successful", false);
            resultJsonMap.put("errorCode", errorCode);
            resultJsonMap.put("errorMsg", errorMsg );

            value = resultJsonMap;
            log.info(value.toString());
        }
        return value;
    }
com.test.service.TestService
public class TestService {
    private static ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    public String runTest() {
        throw new IllegalArgumentException("這是測試");
    }

    public static void main(String[] args) {
        TestService ts = ac.getBean("testService", TestService.class);
        ts.runTest();
    }
}
參考資料

2015年3月4日 星期三

Java ResourceBundle 多語系(本地化)

http://villebez.logdown.com/posts/2014/07/25/java-resourcebundle

這個類提供軟體國際化的捷徑。通過此類,可以使您所編寫的程式可以:
輕鬆地當地語系化或翻譯成不同的語言
一次處理多個語言環境
以後可以輕鬆地進行修改,支援更多的語言環境
這個類的作用就是讀取資源屬性檔(properties),然後根據.properties檔的名稱資訊(當地語系化資訊),匹配當前系統的國別語言資訊(也可以程式指定),然後獲取相應的properties檔的內容。
使用這個類,要注意的一點是,這個properties檔的名字是有規範的:一般的命名規範是:自訂名_語言代碼_國別代碼.properties,如果是默認的,直接寫為:自訂名.properties
例如:
example.properties
example_en_US.properties
example_zh_TW.properties
範例:
定義資源檔,放到src的根目錄下面
example_en_US.properties
aaa = Hello
example_zh_TW.properties
aaa = \u54C8\u56C9
public static void main(String[] args) {
         Locale locale1 = new Locale("zh", "TW"); 
     ResourceBundle resb1 = ResourceBundle.getBundle("example", locale1); 
     System.out.println(resb1.getString("aaa")); 

     Locale locale2 = new Locale("en", "US"); 
     ResourceBundle resb2 = ResourceBundle.getBundle("example", locale2); 
     System.out.println(resb2.getString("aaa")); 
}
執行結果:
哈囉
Hello
Eclipse Plugin:ResourceBundleEditor
好用的properties編輯工具,可以同時開啟不同語系的properties檔案,自動同步key,方便編輯多語系value,並會自動將文字轉換為Java Unicode Escape的格式

Apache Thrift

Command:thrift --gen java
Example.thrift



include "example2.thrift"
namespace java com.thrift.example
typedef i32 int  
typedef i64 long

service ThriftAuthService {  
    example2.XXX auth(1:string account, 2:string password),
}

2015年3月2日 星期一

Gradle Build Project Example

  • Build.gradle : Simple Java Project Example

apply plugin: 'java'
sourceSets {
    main {
        java {
            srcDir 'src'
        }
        resources {
            srcDir 'src'
        }
    }
}
repositories {
    maven {
        credentials {
            username 'deployment'
            password ''
        }
        url "http://127.0.0.1:8081/nexus/content/groups/public/"
    }
}
 
dependencies {
    compile 'junit:junit:4.11'
    …
    …
}

  • Build.gradle : Web Project Example

apply plugin: 'eclipse-wtp'
apply plugin: 'jetty'

  • Build.gradle : Application Project Example

apply plugin: 'application'

def createScript(project, mainClass, name) {
  project.tasks.create(name: name, type: CreateStartScripts) {
    outputDir       = new File(project.buildDir, 'scripts')
    mainClassName   = mainClass
    applicationName = name
    classpath       = project.tasks[JavaPlugin.JAR_TASK_NAME].outputs.files + project.configurations.runtime
}

project.tasks[name].dependsOn(project.jar)

  project.applicationDistribution.with {
    into("bin") {
      from(project.tasks[name])
      fileMode = 0755
    }
  }
  
project.tasks[name].doLast {
    unixScript.text = unixScript.text.replace("\$CLASSPATH", ".:\$APP_HOME/lib/*") 
    //windowsScript.text = windowsScript.text.replace("%CLASSPATH%", ".;%APP_HOME%\\lib\\*") 
}
}

startScripts.enabled = false
run.enabled = false

createScript(project, 'com.gigabyte.HelloWorld ', 'helloworld')

2015年2月4日 星期三

Gradle & Nexus

Logdown -> http://villebez.logdown.com/posts/2015/01/27/gradle
最近為了改善程式庫相依、專案建置、佈署效率與正確的版本控管,所以研究了 Gradle 自動化工具。
另外,為了做部門的 Local Repository ,方便上傳、使用自行開發的 lib,所以建置了 Nexus 。
關於Nexus的安裝啟用,官網 nexus-book 已經有很詳細的參考說明了,
step by step就可以執行一個基本的Repository Server了。
推薦qrtt1在Codedata發表一系列關於Gradle的文章,【認識 Gradle】
從ant -> Maven -> Gradle,利用範例講解的很詳細。
如果要了解專案相依管理,可以直接跳到這篇 【認識 Gradle】(7)Java 專案相依管理
這篇並不是要介紹這兩個工具,
只是個紀錄,紀錄用了Gradle遇到的問題與解決方式,所以請先對Gradle有初步的了解。
這些問題可能也是大家剛開始使用常會遇到的,希望可以幫到你/妳。
Q1: apply plugin 是什麼?? 有哪些可以使用
A1.1: 類似於import, include,也就是 reuse logic
A1.2: 請參考這裡,更進階可以自行客製Plugin
Q2: 程式中文註解,造成編譯失敗
A2: 請服用~ compileJava.options.encoding = 'UTF-8'
Q3: 在MVNrepository找不到想要的libs
A3: 幾個方法,1. 上傳到自行建置的Repository Server,2. include local jar file
Q4: 什麼是providedCompile,跟compile有什麼差別
A4: providedCompile也是編譯時,專案會用到的jar,但跟compile最大的差異在,宣告為providedCompile的jar file不會打包到war file,所以當我的專案需要用到tomcat-catalina libs時,如果將它宣告為compile,則war檔在tomcat server執行時會造成library conflict錯誤。
Q5: 如何改變webapp dir
A5: project.webAppDirName = 'WebContent'

Java Thread Pool

Logdown -> http://villebez.logdown.com/posts/2014/10/16/238021
這篇主要還是延續前篇 JAVA POP3 Server 實作,改善效能問題。
其實我的問題就如同 Gossip@Openhome 的 Design Pattern: Thread Pool 模式 教學說明的第一段,如下:
「在 Thread-Per-Message 模式 中,每次請求來到,就建立一個新的執行緒,用完就不再使用,然後執行緒的建立需要系統資源,對於一個接受許多請求的情況,不斷的建立新執行緒,會導致系統 效能的降低。」
有興趣看 Thread Pool 的演進,可以看看以下這三個 Design Pattern
  1. Thread-Per-Message 模式
  2. Worker Thread 模式
  3. Thread Pool 模式
但是這裡我沒有要實作 Thread Pool 模式,我只是要了解原理跟用途就好,因為 JavaSE 5.0 以後,已經有 util 可以直接達到 Thread Pool的效果了,也就是 concurrent util
所以為了這個效能問題呢,將程式改寫,並加上 Monitor Thread 程式來監看 Thread Pool 使用情形。

不廢話,直接看程式,其他自己看 java api


...

    public void run() {
        // creating the ThreadPoolExecutor

        ThreadPoolExecutor executorPool = new ThreadPoolExecutor(50, 300, 10,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(30),
                Executors.defaultThreadFactory());
        // start the monitoring thread

        MonitorThread monitor = new MonitorThread(executorPool, 5);
        Thread monitorThread = new Thread(monitor);
        monitorThread.start();

        try {
            while (keeprunning) {
                Socket clientSocket = listenSocket.accept();
                clientSocket.setSoTimeout(10000);
                clientSocket.setTcpNoDelay(true);
                executorPool.execute(new manageconnection(clientSocket));
            }
        } catch (IOException excpt) {
            log.error("Sorry ,Failed I/O:" + excpt);
        }
    }

...


MonitorThread.java


public class MonitorThread implements Runnable {
    private static final Logger log = Logger.getLogger(MonitorThread.class);
    private ThreadPoolExecutor executor;

    private int seconds;

    private boolean run = true;
    
    public void shutdown() {
        this.run = false;
    }

    public MonitorThread(ThreadPoolExecutor executor, int delay) {
        this.executor = executor;
        this.seconds = delay;
    }

    @Override
    public void run() {
        while (run) {
            log.info(String
                    .format("[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s",
                            this.executor.getPoolSize(),
                            this.executor.getCorePoolSize(),
                            this.executor.getActiveCount(),
                            this.executor.getCompletedTaskCount(),
                            this.executor.getTaskCount(),
                            this.executor.isShutdown(),
                            this.executor.isTerminated()));
            try {
                Thread.sleep(seconds * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}


output
[monitor] [50/50] Active: 1, Completed: 3, Task: 4, isShutdown: false, isTerminated: false