Fork me on GitHub

1/16/2014

[JPA] Use refresh to get updated data after database trigger

Today I met the issue about EntityManager cache issue.

Let's say that I have 2 tables comment and commant_like. I have a database trigger which increment the field like_count of comment when inserting one new comment_like record.

Below is the orignal code looks like:

In my expectation, the save call will make the database trigger to update the like_count in the comment, but it doesn't.

public void addLikeOnComment(...) {

    commentLikeDao.save(newEntity);
    
    Comment comment = commentDao.find(id);    
    
}

After digging into the code, I found the find has been called somewhere before addLikeComment. so the entity manager will use the cached version message.

public void doWork(...) {

    commentDao.find(id);

    addLikeOnComment(...);

}

The solution is simple. Use the entity manager refresh, to load data from database instead of the cached version

public void addLikeOnComment(...) {

    commentLikeDao.save(newEntity);
    
    Comment comment = commentDao.find(id); 
    commentDao.getEntityManager().refresh(comment)
    
}

-- EOF --

1/09/2014

[Vim] Map Ctrl+S to save file

Beacuse pressing Ctrl-S in terminal will performs an 'XOFF' which stops commands from being receiv

ed. (If Ctrl-S freezes your terminal, type Ctrl-Q to get it going again.)

So, we need to disable that first, put below command in .bashrc

stty stop undef

Then, in the vimrc, add below lines

noremap <silent> <C-S>          :update<CR>
vnoremap <silent> <C-S>         <C-C>:update<CR>
inoremap <silent> <C-S>         <C-O>:update<CR>

It's done. The Ctrl+S has been mapped to :w

References:

  1. In vim how to map “save” to ctrl-s
  2. How to map Ctrl+S in vim on gnome-terminal?

-- EOF --

1/01/2014

[Hibernate] Composite primary key maps composite foreign key

The blog records how to map two entities by the composite keys by using hibernate. Let's say we have 2 entities PrizeItem and PrizeWinnerInfo. We want to map them as OneToOne relation.

The PrizeWinnerInfo (pzwininfo_pzitem_pz_id, pzwininfo_pzitem_id) refer to PrizeItem (pzitem_pz_id, pzitem_id) with the composite foreign key.

Step 0.

Database schema of PrizeItem, table name: ix_prize_item

+----------------------------+--------------+------+-----+---------+-------+
| Field                      | Type         | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+-------+
| pzitem_pz_id               | bigint(20)   | NO   | PRI | NULL    |       |
| pzitem_id                  | int(11)      | NO   | PRI | NULL    |       |
| pzitem_expiration          | datetime     | YES  |     | NULL    |       |
| pzitem_won                 | tinyint(1)   | NO   |     | 0       |       |
| pzitem_won_time            | datetime     | YES  |     | NULL    |       |
| pzitem_winner_info_applied | tinyint(1)   | NO   |     | 0       |       |
| pzitem_pzcha_id            | int(11)      | YES  | MUL | NULL    |       |
| pzitem_serial              | varchar(255) | YES  |     | NULL    |       |
+----------------------------+--------------+------+-----+---------+-------+

Database schema of PrizeWinnerInfo, table name: ix_prize_winner_info

+------------------------+--------------+------+-----+---------+-------+
| Field                  | Type         | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+-------+
| pzwininfo_pzcha_id     | int(11)      | NO   | MUL | 0       |       |
| pzwininfo_pzitem_pz_id | bigint(20)   | NO   | PRI | 0       |       |
| pzwininfo_pzitem_id    | int(11)      | NO   | PRI | 0       |       |
| pzwininfo_name         | varchar(255) | NO   |     | NULL    |       |
| pzwininfo_email        | varchar(128) | NO   |     | NULL    |       |
| pzwininfo_phone        | varchar(32)  | YES  |     | NULL    |       |
| pzwininfo_detail       | longtext     | YES  |     | NULL    |       |
+------------------------+--------------+------+-----+---------+-------+

Step 1.

Tip: Use @Embeddable to define the composite key

The composite primary key PrizeItemPK of PrizeItem

@Embeddable
public class PrizeItemPK implements Serializable
{
    private final static long serialVersionUID = 1L;

    @Column(name="pzitem_pz_id")
    private Long prizeId;

    @Column(name="pzitem_id")
    private Integer prizeItemId;

    public PrizeItemPK() {}
    public PrizeItemPK(Long prizeId, Integer prizeItemId) {
        super();
        this.prizeId = prizeId;
        this.prizeItemId = prizeItemId;
    }

    @JsonGetter("prize_id")
    public Long getPrizeId() {
        return prizeId;
    }

    public void setPrizeId(Long prizeId) {
        this.prizeId = prizeId;
    }

    @JsonGetter("prize_item_id")
    public Integer getPrizeItemId() {
        return prizeItemId;
    }

    public void setPrizeItemId(Integer prizeItemId) {
        this.prizeItemId = prizeItemId;
    }

}

The composite primary key PrizeWinnerInfoPK of PrizeWinnerInfo

@Embeddable
public class PrizeWinnerInfoPK implements Serializable
{
    private final static long serialVersionUID = 1L;

    @Column(name="pzwininfo_pzitem_pz_id")
    private Long prizeId;

    @Column(name="pzwininfo_pzitem_id")
    private Integer prizeItemId;

    public PrizeWinnerInfoPK() {}
    public PrizeWinnerInfoPK(Long prizeId, Integer prizeItemId) {
        super();
        this.prizeId = prizeId;
        this.prizeItemId = prizeItemId;
    }

    @JsonGetter("prize_id")
    public Long getPrizeId() {
        return prizeId;
    }

    public void setPrizeId(Long prizeId) {
        this.prizeId = prizeId;
    }

    @JsonGetter("prize_item_id")
    public Integer getPrizeItemId() {
        return prizeItemId;
    }

    public void setPrizeItemId(Integer prizeItemId) {
        this.prizeItemId = prizeItemId;
    }

}

Step2.

Tip1: Use @OneToMany mapping, not @OneToOne
Tip2: Use @JsonManagedReference and @JsonBackReference to avoid cycling json serialization

ORM entity of PrizeItem

@Entity
@Table(name="ix_prize_item")
@JsonAutoDetect(getterVisibility=JsonAutoDetect.Visibility.NONE)
public class PrizeItem
{
    @EmbeddedId
    private PrizeItemPK pk;

    ...

    @OneToMany(mappedBy="prizeItem", fetch=FetchType.EAGER)
    @JsonBackReference
    private List winnerInfo;

}

ORM entity of PrizeWinnerInfo

@Entity
@Table(name="ix_prize_winner_info")
@JsonAutoDetect(getterVisibility=JsonAutoDetect.Visibility.NONE)
public class PrizeWinnerInfo extends AbstractExtensibleJsonAliasBean
{
    @EmbeddedId
    private PrizeWinnerInfoPK PK;

    ...

    @ManyToOne(fetch=FetchType.EAGER)
    @JoinColumns ({
        @JoinColumn(name="pzwininfo_pzitem_pz_id", referencedColumnName = "pzitem_pz_id", insertable = false, updatable = false),
        @JoinColumn(name="pzwininfo_pzitem_id", referencedColumnName = "pzitem_id", insertable = false, updatable = false)
    })
    @JsonManagedReference
    private PrizeItem prizeItem;

}

2014/01/16 Note:

I found the use of @JsonBackReference will make the serialization fail on that field. So, I took the below solution (http://stackoverflow.com/a/10111411/474002)

References:

  1. java - hibernate composite Primary key contains a composite foreign key, how to map this - Stack Overflow
  2. java - @OneToOne annotation within composite key class is not working - Stack Overflow
  3. @JoinColumn is part of the composite primary keys | dwuysan

10/17/2013

How the Economic Machine Works


一個蠻通俗易懂的經濟解釋影片, 釐清了不少先前似懂非懂的疑惑, 以下筆記 ...
  • 三個經濟波動的主要的推力:
    1. 生產力成長
    2. 短期 debt cycle
    3. 長期 debt cycle
  • Transaction 是驅動經濟發展的基石, 每個 transaction 都由一個買方跟一個賣方組成, 之間交換 money/credit 來取得貨物, 服務或者其他金融資產
  • Buyer <-> Seller 等同於 Lender <-> Borrower
  • Lender 想由借出的錢賺取更多的錢, Borrower 想要取得更多的錢去得到他所想要的!
  • Lender 付出了 money 取得了 Borrower 的 credit, Borrower 付出了信用換到了錢去買他想要的東西, 但是同時也承擔了未來要償還本金和利息的義務 (debt)
  • 為什麼 credit 很重要, 因為有了 credit 你的花費就允許超過你的收入, 而花費就是驅動經濟的主因!因為一個人的花費就是其他人的收入, 當你有更多的花費代表別人有更多的收入, 有了更多的收入, 銀行就會比較信任然後借錢給你, 也就是你變得比較有 credit
  • increase income -> increase borrowing -> increase spending 不斷循環 (cycles)
  • 在沒有 credit 的時候, 增加 income 的唯一辦法是增加 productivity, 但是可以用 credit 來 borrow 的話, 不需要增加 productivity 也可以增加 income, 進而造成正向循環. 但是如果你用 credit 取得的額外收入拿去做的花費, 不能讓你生產更多的話, 遲早你需要付出代價償還
  • 當市場上的 spending 增加的速度大於 product 生產的速度, 價格就會上漲 -> inflation (通貨膨脹), 中央銀行可以透過調高利率來降低 borrow 的意願, 抑制通貨膨脹. 反過來說, 中央銀行也可以透過降低利率來刺激 borrow 進而刺激 spending
  • short-term debt 就是中央銀行透過調節利率來平衡 credit 的循環. 但儘管如此, 相較於償還 credit, 人類樂觀的天性更傾向於 借多花多以後再說, 所以 short-term debt 仍會慢慢累積越來越多的 debt 超出 income 進而造成 long-term debt
  • 當然, 在 debt 增加但是 income 也對應增加的情況下, 一切仍然是 under control, 這時候就會有一種大家都很有錢的假象. 大家的 income 因為 borrow 都很充裕, 可以拿去投資, 相對的有價資產價格就會飆升, 資產擁有者就覺得自己很有錢. 直到某個時間點, debt 增加的速度高過了 income, 就崩盤了
  • 人們開始 less spending -> less income -> less wealth -> less credit -> less borrowing ->, 這時候降低利率已經沒有用了, 因為重點是收入變少了, 就算借錢是 0 利率也無法幫助還清之前的 debt
  • 面對這種崩盤能有什麼對策:
    1. people, businesses, government cut their spending
    2. debt are reduced through defaults and reconstructings (lender 可以接受償還較少或是以較長的時間償還)
    3. wealth is redistributed
    4. the central bank prints new money
  • 1, 2, 3 都有可能會附帶造成負循環或者不良的社會影響, 而 4 則是刺激正向循環的一個手段. 中央銀行印鈔票去買財務資產, 資產價格上升使得個人的 credit 提昇, 這僅幫助了擁有資產的人. 而中央政府可以付錢買貨物或服務, 那些提供貨物和服務的人可以賺到政府的錢, 但中央政府不能印鈔票. 所以這兩者必須合作. 中央銀行向中央政府購買政府債卷, 即中央銀行借錢給中央政府, 中央政府拿錢取補助失業, 做刺激消費方案(消費卷..等等), 同時也增加了政府的負債. 但是這可以減輕整體 debt
  • 最後給個人的一些建議
    1. Don't have debt rise faster than income
    2. Don't have income rise faster than productivity
    3. Do all that you can to raise your productovity

6/27/2013

HTTP POST and PUT in REST way

HTTP 的 Get/Post/Delete/Put 對應到 Resource 的 CRUD (Create, Retrieve, Update and Delete),其中 POST 和 PUT 最常讓人摸不著頭緒,不知道什麼時候該用什麼。

今天 survey 了一下網路的資料,大概可以歸納出如下兩張圖,未來在遇到相關的設計時可以參照琢磨琢磨。

http-post

http-put

References

  1. SO: PUT vs POST in REST
  2. PUT or POST: The REST of the Story

6/01/2013

Deploy Jersey in the Openfire plugin

Let's say, the original method to access the openfire service by http is by HttpServlet (ex. presence plugin). So, how to replace it with Jersey for convenient development? In this post, I will detail the steps and the problems I met during achieving this.

The main reference is here. Assuming that you have successfully run a self-made plugin.


Step1. Modify the web-custom.xml

The web-custom.xml tells the container where to search for resource class. From that reference. There is some tricks in the web-custom.xml processing in openfire. So we need a wrapper, and finding the true resource class package there. As below. The class in the <servlet-class> tag is your wrapper class, the path in <url-pattern> is the base url you want.

<!-- Servlets -->
<servlet>
    <servlet-name>BrownyServlet</servlet-name>
    <servlet-class>com.brownylin.openfire.plugin.browny.BrownyServletWrapper</servlet-class>
</servlet>

<!-- Servlet mappings -->
<servlet-mapping>
    <servlet-name>BrownyServlet</servlet-name>
    <url-pattern>/test</url-pattern>
</servlet-mapping>

Step2. Tell Wrapper where to find resource class

The trick in the wrapper class is to find resouce class by PackagesResourceConfig. Below sample is referenced from that link.

The resouce class should be under the same package (or inner) as the wrapper class.

AuthCheckFilter.addExclude(SERVLET_URL); is used for avoiding the need of login to access the api.

    
public class BrownyServletWrapper extends ServletContainer {

    private static final long serialVersionUID = 1L;
    private static final String SERVLET_URL = "browny/test/*";
    private static final String SCAN_PACKAGE_KEY = "com.sun.jersey.config.property.packages";
    private static final String SCAN_PACKAGE_DEFAULT = BrownyServletWrapper.class
        .getPackage().getName();

    private static final String RESOURCE_CONFIG_CLASS_KEY = "com.sun.jersey.config.property.resourceConfigClass";
    private static final String RESOURCE_CONFIG_CLASS = "com.sun.jersey.api.core.PackagesResourceConfig";

    private static Map<String, Object> config;
    private static PackagesResourceConfig prc;

    static {
        config = new HashMap<String, Object>();
        config.put(RESOURCE_CONFIG_CLASS_KEY, RESOURCE_CONFIG_CLASS);
        config.put(SCAN_PACKAGE_KEY, SCAN_PACKAGE_DEFAULT);
        prc = new PackagesResourceConfig(SCAN_PACKAGE_DEFAULT);
        prc.setPropertiesAndFeatures(config);
        prc.getClasses().add(BrownyResources.class);
    }

    public BrownyServletWrapper() {
        super(prc);
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);

        // Exclude this servlet from requering the user to login
        AuthCheckFilter.addExclude(SERVLET_URL);
    }

    @Override
    public void destroy() {
        super.destroy();
        // Release the excluded URL
        AuthCheckFilter.removeExclude(SERVLET_URL);
    }
}

Step3. The Jersey dependent jar library

They are asm-3.3.1.jar, jersey-bundle-1.10-b01.jar, jersey-servlet-1.17.1.jar, jsr311-api-1.1.1.jar. If wrong dependency, the ClassNotFoundException, ClassNotDefException will happen (you could find the error from openfire_src/target/openfire/logs/error.log)


Step4. The Resource class

The trick here is the @Path() should starts from plugin name, later servlet url-pattern and at the end - path of the your resouce. So that is @Path("browny/test/hello")

    
package com.brownylin.openfire.plugin.browny;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("browny/test/hello")
public class BrownyResources {

    @GET
    @Path("/")
    public Response getMsg() {

        String output = "Jersey say Hello";
        return Response.status(200).entity(output).build();

    }
}

Step5. Don't want result shown in openfire admin console

As this thread said. The default configuration is to capture all text/html content from the server and force it into the admin page.

You could add the url pattern you want to exclude in openfire_src/src/web/WEB-INF/decorators.xml as below

<decorators defaultdir="/decorators">
    <decorator name="setup" page="setup.jsp">
        <pattern>/setup/*.jsp</pattern>
    </decorator>
    <decorator name="main" page="main.jsp">
          <pattern>/*.jsp</pattern>
          <pattern>/plugins</pattern>
    </decorator>
    <decorator name="none"/>
    <excludes>
        <pattern>/setup/setup-completed.jsp*</pattern>
        <pattern>/setup/setup-ldap-server_test.jsp*</pattern>
        <pattern>/setup/setup-ldap-user_test.jsp*</pattern>
        <pattern>/setup/setup-ldap-group_test.jsp*</pattern>
        <pattern>/setup/setup-clearspace-integration_test.jsp*</pattern>
        <pattern>/setup/setup-admin-settings_test.jsp*</pattern>
        <pattern>/login.jsp*</pattern>
        <pattern>/plugin-icon.jsp*</pattern>
        <pattern>/js/jscalendar/i18n.jsp*</pattern>
        <pattern>/plugins/browny/test/*</pattern>
    </excludes>
</decorators>

Step6. Test the resource url

Last, test the url localhost:9090/plugins/browny/test/hello. It's done. Hope this will be useful to someone :)

-- EOF --