JPA — Как избежать получения пустого списка?

#list #jpa #ejb

#Список #jpa #ejb

Вопрос:

Я создаю что-то вроде сайта социальной сети, например Facebook, в качестве университетского проекта. Пользователи могут загружать фотографии, но я почему-то не могу получить список фотографий для конкретного пользователя.

Вот как я делаю это прямо сейчас:

 @Entity
@Table(name = "users")
public class User implements Serializable {

@Id
private String emailAddress;
private String password;
private String firstName;
private String lastName;

(...)

@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
private List<Photo> photos;

public User() {
}

(...)

public void addPhoto( Photo photo){
    photos.add(photo);
}

public List<Photo> getPhotos() {
    return photos;
}
}
 

А вот объект Photo:

 @Entity
public class Photo implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String url;
private String label;     
@ManyToOne
private User owner;

public Photo() {
}

(...)

public User getOwner() {
    return owner;
}    
}
 

Каждая фотография загружается путем создания записи, которая ее содержит. Вот EJB, который это делает:

 @Stateless
public class PublicPost implements PublicPostRemote {

@PersistenceContext
EntityManager em;

@Override
public void createPost(LoginUserRemote loginUserBean, String targetEmail, final String content, final String photoURL) {
    if (loginUserBean.isLoggedIn()) {
        final User author = loginUserBean.getLoggedUser();
        System.out.println(targetEmail);
        final User target = em.find(User.class, targetEmail);
        if (author != null amp;amp; target != null) {
            //See if there's a photo to post as well
            Photo photo = null;
            if (photoURL != null) {
                photo = new Photo(photoURL, author, content);
                em.persist(photo);
            }

            MessageBoard publicMessageBoard = target.getPublicMessageBoard();
            Post post = new Post(author, content);
            post.setMessageBoard(publicMessageBoard);
            if (photo != null) {
                post.setPostPhoto(photo);
            }
            em.persist(post);
            em.refresh(publicMessageBoard);
            //Send an e-mail to the target (if the author and the target are different)
            if (!author.getEmailAddress().equals(target.getEmailAddress())) {
                final String subject = "[PhaseBook] "   author.getEmailAddress()   " has posted on your public message board.";
                Thread mailThread = new Thread() {

                    @Override
                    public void run() {
                        try {
                            GMailSender.sendMessage(target.getEmailAddress(), subject, content);
                        } catch (MessagingException ex) {
                            Logger.getLogger(PublicPost.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                };
                mailThread.start();
            }
        }
    }
}
}
 

Итак, что происходит: я создаю новую запись, содержащую фотографию, но позже, когда я использую это, на веб-уровне…

 LoginUserRemote lur = (LoginUserRemote)session.getAttribute("loginUserBean");
User user = lur.getLoggedUser();
List<Photo> photos = user.getPhotos();
System.out.println();
System.out.println("This user has this many photos: "   photos.size());
 

… он всегда говорит мне, что у пользователя 0 фотографий. Почему это так? Я неправильно определяю связь между пользователем и фотографией? Я забыл что-нибудь сохранить / обновить? Или проблема кроется где-то в другом месте?

Ответ №1:

Если вы сохраняете объект отдельного пользователя (вошедшего в систему пользователя) в сеансе HTTP, а затем создаете и сохраняете фотографии с этим отдельным пользователем в качестве владельца, JPA не будет автоматически добавлять фотографию отдельному пользователю. Для менеджера сущностей этот отдельный пользователь не существует: он больше не находится под его ответственностью.

Даже если пользователь все еще был подключен, вы несете ответственность за поддержание согласованности графа объектов. Если вы измените одну сторону ассоциации (установив пользователя в качестве владельца фотографии), вам также следует изменить другую сторону (добавив фотографию в список фотографий владельца).

Я не совсем уверен, что это причина проблемы, потому что вы не показали нам, что такое loginUserBean и что он сделал, чтобы получить вошедшего в систему пользователя, но это может быть ответом.

Ответ №2:

Здесь есть ряд проблем:

  • Действительно ли фотографии хранятся в базе данных? Может быть, у вас не открыта транзакция?
  • Вы не обновляете обе стороны ассоциации.

Теоретически вам нужно только обновить сторону владельца, но лучше перестраховаться, чем потом сожалеть:

 photo = new Photo(photoURL, author, content);
em.persist(photo);
author.addPhoto(photo);
 
  • Вы извлекаете пользователя из сеанса, а затем извлекаете связанную коллекцию фотографий. Вы действительно знаете, что это значит? Если у пользователя сотни фотографий, вы действительно хотите постоянно хранить их в сеансе HTTP вместе с пользователем? Facebook работает не так ;-).

Я думаю, что обновление вашей сущности (с em.refresh(lur.getLoggedUser()) помощью) может сработать, но только в университете, а не в реальной жизни. Загрузка всех пользовательских фотографий сразу в память — это перебор. Лично я бы даже удалил photos ассоциацию от пользователя, чтобы избежать этого. Загружайте по одной странице за раз и только по требованию.

  • Даже если вы знаете, что делаете, или такое поведение приемлемо, объекты, хранящиеся в сеансе HTTP, так называемые отделены от контекста сохранения, что означает, что ваш поставщик сохранения больше не отслеживает их. Таким образом, добавление фотографии не означает, что photos коллекция будет волшебным образом обновляться в каждом объекте. Я тщательно обдумываю, это было бы еще хуже.
  • И последнее, но не менее важное: вам createPost() действительно нужен некоторый обзор кода. Он выполняет по крайней мере 4 вещи одновременно System.out , одноразовые потоки, созданные по требованию, молча ничего не делая, когда не выполняются предварительные условия (например, пользователь не входит в систему, отсутствуют параметры), смешивая проблемы на разных уровнях абстракции. Не хочу быть слишком дотошным, но на вашу оценку может повлиять качество кода.