2016-11-03 6 views
0

У меня есть AngularJS переднего конца, который делает несколько запросов к различным ресурсам на Java фонового работает Wildfly 10.JPA запрос не находя объект после того, как сохраняется

Каждый конечной ресурс запрашивает базу данных (MySQL 5.6), чтобы найти пользователь использует уникальный идентификатор пользователя из токена доступа (это не первичный ключ, он имеет уникальный индекс).

В случае, если пользователь не найден в базе данных, я создаю его из маркеров информации доступа, см ниже:

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; 
    } 

    ... 
} 

Я создал синглтон, чтобы убедиться, что пользователь только создается, если это действительно Безразлично Уже существует.

@Singleton 
@Startup 
public class UserRegistrationService { 

    @PersistenceContext 
    EntityManager em; 

    @EJB 
    UserService userService; 

    public User findOrCreateUser(AccessToken token) { 
     String kcId = token.getUserId(); 

     User user = this.findByKcId(kcId); 
     if (user == null) { 
      user = new User(); 
      user.setKcId(kcId); 

      ... 

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

     return user; 
    } 

    private User findByKcId(String kcId) { 

     CriteriaBuilder cb = em.getCriteriaBuilder(); 

     CriteriaQuery<User> criteria = cb.createQuery(User.class); 
     Root<User> user = criteria.from(User.class); 
     criteria.select(user).where(cb.equal(user.get(User_.kcId), kcId)); 

     List<User> users = em.createQuery(criteria) 
       .setMaxResults(1) 
       .getResultList(); 
     if (users.isEmpty()) { 
      return null; 
     } 

     return users.get(0); 
    } 

    ... 
} 

Но по какой-то причине первого раза знаки в системе пользователя, все запросы (наша домашняя страница делает 3 запроса асинхронных) запускает INSERT в базе данных, что приводит к:

MySQLIntegrityConstraintViolationException: Duplicate entry ... 

Несмотря на то, первый запрос уже создал нового пользователя в базе данных. После первого входа все работает нормально.

Любые идеи?

UPDATE:

Я создал метод внутри UserRegistrationService для поиска пользователей и один и тот же EM с помощью метода findOrCreateUser.

Также здесь есть EntityManager хэш-код() Выход:

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 

UPDATE 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 разных ЭМ (и, следовательно, 2 разных транзакции). –

+0

@SteveEbersole только что отредактировал мой код. Я ввел EntityManager в абстрактный класс. Кроме того, они представляют собой 3 разных запроса, не являются ли они уже 3 различными транзакциями по одному для каждого запроса? – Bruno

+0

Я действительно не понимаю, почему «AbstractService» имеет свои классы расширения в качестве членов. Это я, как недостаток дизайна (или, скорее, недостаток), для меня. – Antoniossss

ответ

0

(our home page makes 3 async requests)

Я предполагаю, что эти просьбы вызываются не 1 один все на быт сразу направо (асинхронный в конце концов)

Так проще говоря вы получили гоночные условия. Чтобы доказать, если я правильно или неправильно, для испытаний, сделать findOrCreate метод synchronized так 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 (несколько асинхронных вызовов), скорее всего, придется синхронизировать.

+0

Только что отредактировал мой пост, пожалуйста, посмотрите. – Bruno

+0

@Bruno Я администратор, он выглядит законным. Поэтому, если это не гоночные условия, у меня нет идей. – Antoniossss

+0

Спасибо за помощь! – Bruno