Ghetolay
Ghetolay

Reputation: 3342

Manage persistent user's data across web request

I'm working in a WebSocket server with user authentication. I don't really now how to store this user among request. My WebSocket connection will probably stay open for hours and sometimes it will receive 100 request per minute and sometimes just 2 per hours (it's unpredictable). The user data may be changed by another application, this is not my question but it may be relevant. I'm using spring 3 and hibernate 3.6.9.

My question is : Should I use a specific transaction management and how do I manage these user data.

I've test 2 use case :

1. Load my user entity at log in and merge() it in each request. I have to merge it because transaction are closed after each request. Merge is not a good solution because it do not load the database entity but persist the one merged. So if the data where update somewhere else it'll revert them and not load the updated one.

2. At log in I only store the user ID and I find() my user at each request. To avoid database request flood I've set a second level cache but according to my sql server log hibernate still run a request per find().

Here is some concrete code and configuration :

persistence.xml

<persistence-unit name="greenpacs">
<provider>org.hibernate.ejb.HibernatePersistence</provider>

<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
    <property name="hibernate.archive.autodetection" value="class"/>
    <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
    <property name="hibernate.hbm2ddl.auto" value="update"/>
    <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>

    <property name="hibernate.show_sql" value="true"/>
    <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/db"/>
    <property name="hibernate.connection.user" value="root"/>
    <property name="hibernate.connection.password" value="password"/>
    <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
</properties>
</persistence-unit>

applicationContext.xml

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="greenpacs" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

User entity

@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class User {
@Id
@Column(name = "userId", unique = true, nullable = false)
private Integer id;

private String pwd;

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "User_Role", joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "userId") }, inverseJoinColumns = { @JoinColumn(name = "role_id", referencedColumnName = "id") })
private List<Role> roles;

@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="id")
private Person person;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user", cascade = CascadeType.ALL)
private List<Pacs> pacs;

// getter&setter

WebSocket Listener

@Component
public class UserManager implements WampMessageHandler {

UserService userService;
//Autowired setter

    User user;

@Override
public void onConnected(WampConnection connection) {
       //loginmethod return userId
       user = userService.find(userId);
    }

@Transactional
@Override
public boolean onMessage(String sessionId, int messageType, Object[] msg)
        throws BadMessageFormException {
    user = userService.merge(user);  
            //or  
            user = userService.find(user.getId());  
            ...
}

My UserService just redirect to EntityManager's methods.

Upvotes: 0

Views: 208

Answers (1)

JB Nizet
JB Nizet

Reputation: 691865

Your second solution looks like the obvious one. You're seeing a query to the database because you didn't set the hibernate.cache.use_second_level_cache property to true.

Upvotes: 1

Related Questions