I have user login info stored with JPA along with a room. The room has a list of users, the users have a transient field, which is where my problem is coming in.

@Entity
public class User{

    //some other stuff ids and such


    @Id
    @GeneratedValue(generator = "increment")
    @GenericGenerator(name = "increment", strategy = "increment")
    public Long getId() {
        return id;
    }

    @ManyToOne
    public Room getCurrentRoom() {
        return currentRoom;
    }

    public void setCurrentRoom(Room currentRoom) {
        this.currentRoom = currentRoom;
    }


    @Transient
    private BlockingDeque<Message> getMessages() {
        return messages;
    }

    private void setMessages(BlockingDeque<Message> messages) {
        this.messages = messages;
    }
}

@Entity
public class Room implements Serializable {

    //OTHER STUFF

    private List<Player> players;

    public void sendMessage(Message m){
        Session sess = HibernateUtil.getSessionFactory().openSession();

        for(Player p : players){
            p.addMessage(m);
        }
        sess.close();
    }

    @OneToMany(mappedBy = "currentRoom",fetch = FetchType.EAGER)
    public List<Player> getPlayers(){
        return players;
    }

    private void setPlayers(List<Player> players){
        this.players=players;
    }

}

//THIS IS in the login servlet
        try {
            HttpSession httpsession = request.getSession();
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            Session session = HibernateUtil.getSessionFactory().
                    openSession();
            session.beginTransaction();
            Query query = session.createQuery("from Player P where P.username = :username");
            query.setParameter("username", username);
            List<Player> players = (List<Player>) query.list();
            if(players.size()==1){
                Player p2 = players.get(0);
                Player p = new Player(username, password, p2.getSalt());
                if(p.getPassword().equals(p2.getPassword())){
                    httpsession.setAttribute("user", players.get(0));
                    //USES THE ONE RETURNED FROM HIBERNATE
                }
            } else {

            }
            session.getTransaction().commit();
            response.sendRedirect("./index.jsp");
            session.close();

        } finally {
            out.close();
        }

the problem here is that

User u = session.getAttribute("User");

User other = null;
for(User o : u.getCurrentRoom().getPlayers()){
    if(o.equals(u)){
        other=o;
        break;
    }
}

u.getCurrentRoom().getPlayers().contains(u)
//returns true because u.equals(other)
u==o
//returns false

u.messages==o.messages
u.messages.equals(o.messages)
//these are both false because messages is transient

the messaging system I want doesn't work because of this, how can I ensure that all references returned from JPA/Hibernate reference the same object if they have the same key? Or should I use a different approach?

Recommended Answers

All 2 Replies

Do you override equals for your User class and use that for all "equals" tests? That woud be the normal approach, == is never going to work all the time if objects are being re-created.

Yes, I do overrride equals, and the equality/non-equality isn't really the problem, the problem is that there are duplicates.

JPA/Hibernate is loading the player that logged in, then that players current room, and while loading that room, it Eagerly (temporary fix for LazyInitializationException) loads all the current players in that room.

One of those players is already in memory (the Signed on player), but when it loads the list of players it doesn't use the already existing player, but makes a new one.

This creates a problem when we access a player through his/her session or through a reference to the room, because the Player has a Transient object field (a BlockingQueue for messaging) which needs to be updated (when they send a message) through the room, to send to all players, and read (to recieve a message) through the player in the session.

Is there a way for Hibernate/JPA to cache objects (by id?) so that duplicates are not created and the same reference is returned each time? or should I just access the player like this sessionPlayer.currentRoom.getPlayer(sessionPlayer) to consistantly use the same player object?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.