我有一个servlet,它通过名为DBManager的单例类访问DB。 该类包含对EntityManager的引用。 这是我的代码:
package database;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import com.btc.EMF;
import database.entities.User;
public class DBManager {
public class UserAlreadyExistsException extends Exception{
private static final long serialVersionUID = 1L;}
/* attributes */
private static DBManager instance = null;
private final EntityManager em = EMF.get().createEntityManager();
private int tokens = 1;
private final EntityTransaction transaction = em.getTransaction();
private DBManager(){
}
public static DBManager getInstance(){
if(instance == null){
instance = new DBManager();
}
return instance;
}
@SuppressWarnings("unchecked")
public synchronized boolean createUser(String username, String password) throws UserAlreadyExistsException {
while(tokens == 0){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
tokens--;
Query q = em.createQuery("select u from "+User.class.getName()+" u");
List<User> list = q.getResultList();
if(list != null){
if(list.size()>0){
throw new UserAlreadyExistsException();
}
}
User u = new User();
u.setUsername(username);
u.setPassword(password);
transaction.begin();
em.persist(u);
transaction.commit();
tokens++;
this.notifyAll();
return true;
}
@SuppressWarnings("unchecked")
public synchronized void eraseDB(){
while(tokens == 0){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
tokens--;
Query q = em.createQuery("select u from "+User.class.getName()+" u");
List<User> list = q.getResultList();
transaction.begin();
for(User u : list)
em.remove(u);
transaction.commit();
tokens++;
this.notifyAll();
}
public EntityManager getEm() {
return em;
}
}
serlvet只是按顺序调用eraseDB()
和createUser()
。
我添加了synchronized
方法以及wait()
和notifyAll()
以使并发工作但我失败了。如果我一直在浏览器中按F5,则由于并发问题会出现一些错误。此外,等待和通知的使用似乎过于复杂。
这样做的正确方法是什么?
答案 0 :(得分:1)
基本上,处理Connection
和相关资源的每个对象都必须在最窄的范围内初始化。除此之外,您天真地和非常为您的DBManager
应用单例,这将在多个线程中使用,因为多个请求由单个servlet实例提供(有关此内容的更多信息) :How do servlets work? Instantiation, sessions, shared variables and multithreading)。
最好的选择是从DBManager
类中删除单例(反)模式,并确保在应该处理数据库资源的最窄范围内初始化DBManager
。