2016-11-03 70 views
0

我有一個AngularJS的前端,這使得多個請求不同的資源上運行Wildfly 10JPA查詢沒有找到實體堅持後

每個資源端點查詢數據庫(MySQL的5.6),以找到一個Java後端用戶使用來自訪問令牌的唯一用戶ID(它不是主鍵,它具有唯一索引)。

如果用戶沒有在數據庫中找到,我從訪問令牌信息創建它,看到波紋管:

public abstract class AbstractService { 

    @PersistenceContext 
    protected EntityManager em; 

    ... 

} 

public abstract class AbstractResource extends AbstractService { 

    @EJB 
    UserService userService; 

    @EJB 
    UserRegistrationService userRegistrationService; 

    public User getUser(AccessToken token) { 

     User user = userService.findByKcId(token.getUserId()); 
     if (user == null) { 
      user = userRegistrationService.findOrCreateUser(token); 
     } 

     return user; 
    } 

    ... 
} 

我創建了一個單,以確保它是否真的沒有按用戶只能創建已經不存在了。

​​

但首次某種原因,用戶招牌式,所有的請求(我們的主頁,使3個異步請求)觸發這會導致數據庫的INSERT:

MySQLIntegrityConstraintViolationException: Duplicate entry ... 

即使第一個請求已經在數據庫上創建了新用戶。第一次登錄後,一切正常。

任何想法?

UPDATE:

我創建了一個方法內UserRegistrationService搜索用戶和共享與findOrCreateUser方法相同EM。

而且,這裏是EntityManager的hashCode()方法的輸出:

11:53:32,344 INFO [stdout] (default task-21) UserService.findKcById: 11694883 
11:53:32,416 INFO [stdout] (default task-21) UserRegistrationService.findOrCreateUser: 212546987 
11:53:32,416 INFO [stdout] (default task-21) UserRegistrationService.findKcById: 212546987 
11:53:32,423 INFO [stdout] (default task-20) UserService.findKcById: 11694883 
11:53:32,495 INFO [stdout] (default task-20) UserRegistrationService.findOrCreateUser: 212546987 
11:53:32,495 INFO [stdout] (default task-20) UserRegistrationService.findKcById: 212546987 
11:53:32,553 INFO [stdout] (default task-26) UserService.findKcById: 11694883 

更新2:LOG

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '8f262ed0-3868-449e-aea8-b2af55209479' for key 'kc_id2_UNIQUE' 
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404) 
at com.mysql.jdbc.Util.getInstance(Util.java:387) 
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:934) 
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3870) 
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3806) 
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2470) 
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2617) 
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2550) 
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861) 
at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2073) 
at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2009) 
at com.mysql.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:5094) 
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1994) 
at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:537) 

謝謝!

+0

你如何獲得EntityManager引用?我的猜測是你有2個不同的EM(因此有2個不同的交易)。 –

+0

@SteveEbersole剛編輯我的代碼。我將EntityManager注入了一個抽象類。他們也是3個不同的請求,是不是他們已經有3個不同的事務每個請求一個? – Bruno

+0

我真的不明白爲什麼'AbstractService'將其擴展類作爲成員。它作爲設計(或者說缺乏它)缺陷給我。 – Antoniossss

回答

0

(our home page makes 3 async requests)

我認爲這些要求是由一個BYT一次全部正確調用不是1(異步畢竟)

所以簡單的講,你得到了比賽的條件。 爲了證明,如果我是對還是錯,做檢查,讓findOrCreate方法​​ 所以delcare,如下所示:

public synchronized User findOrCreateUser(AccessToken token) 

應該解決這個問題(廣告NAS副作用,它會序列請求,但是這是一個不同的問題)

+0

我同意它應該是一個競賽條件。我試過了,但沒有奏效。單身人士不應該解決這個問題,因爲它默認爲@Lock(LockType.WRITE)? – Bruno

+0

看起來不像︰http://stackoverflow.com/questions/22493213/concurrent-access-to-a-locklocktype-write-method – Antoniossss

+0

我檢查了這個問題,但'同步'沒有爲我工作。我猜測是與休眠緩存相關的東西。也許插入後的第一個查詢從緩存中獲取結果? – Bruno

0

也許你這裏有邏輯錯誤

User user = userService.findByKcId(token.getUserId()); 
    if (user == null) { 
     user = new User(); 
     ... 

     em.persist(user); 
     em.flush(); 
     em.refresh(user); 
    } 

也許 userService.findByKcId(token.getUserId()); 沒有返回用戶,而應該由於例如。錯誤SELECT查詢。

表日後調查:

  • 顯示我們的用戶創建代碼,其餘
  • 我們展示的確切ConstrainViolation
  • 向我們展示代碼userService.findByKcId

但無論如何,恕我直言,這是找到concurrenc env中的-or-create邏輯(多個異步調用)很可能必須同步。

+0

剛剛編輯我的文章,請看看。 – Bruno

+0

@布魯諾我管它看起來合法。所以如果不是比賽條件,我就沒有想法。 – Antoniossss

+0

感謝您的幫助! – Bruno