【问题标题】:JPA EntityManager doesn't commit changes in SQLite databaseJPA EntityManager 不会在 SQLite 数据库中提交更改
【发布时间】:2016-01-10 07:53:07
【问题描述】:

我有一个 SQLite 数据库。我使用 EclipseLink 和 JPA 来处理它。另外我还有一个实体类User

import com.vaadin.server.VaadinService;
import java.io.Serializable;
import javax.persistence.*;

@Entity
public class User implements Serializable {

    @Id @GeneratedValue
    long id;            // Unique identifier of the user in the DB
    String username;    // Unique name used for login
    String password;    // Password used for login

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public Long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

在注册表单中,我创建了一个用户,然后调用 EntityManagerpersist() 进行更改:

public int createUser(String username, String password, String password_confirmation) {
        int regStatus = 0;
        if(checkValidUsername(username)) {
            if(checkValidPassword(password, password_confirmation)) {
                EntityManager em = factory.createEntityManager();
                try {
                    em.getTransaction().begin();
                        User user = new User(username, password);
                        em.persist(user);
                    em.getTransaction().commit();
                }
                catch(Exception e) {
                    System.out.println(e.getMessage());
                    regStatus = 3;
                }
                finally {
                    em.close();
                }
            }
            else regStatus = 2; // Password mismatch
        }
        else regStatus = 1;     // User with selected username already present in DB

        return regStatus;
    }

它没有任何问题。我在USER 表中获得了每个新注册的用户。但是,当我尝试更改密码时,它不起作用。以下是与此过程相关的方法:

// Inside the controller for my settings view - here the user can change various things related to his/her profile
public void setCurrentUser() {
    currentUser = UserController.findUserByName((String)VaadinSession.getCurrent().getAttribute("username")); // findUserByName() is a static method
}


// Inside the User controller I have multiple methods for common user-related queries; here I use the username that I have retrieved from the VaadinSession's attribute "username" to execute a query and get the User entity (I make sure that a user's name is unique so getting a single result here is not a problem)
public static User findUserByName(String username) {
    if(username == null) return null;
    factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
    EntityManager em = factory.createEntityManager();
    User user = null;

    try{
        Query q = em.createQuery("SELECT u FROM User u WHERE u.username = :username");
        q.setParameter("username", username);
        user = (User)q.getSingleResult();

    }
    catch(Exception e){
        System.err.println(e.getMessage());
    }
    finally {
        em.close();
    }

    return user;
}


// Inside the controller fpr my settings view (where I change the password)
public int changePassword(String currentPassword, String newPassword, String newPasswordConfirmation) {
    if(newPassword.isEmpty()) return 1;
    if(!newPassword.equals(newPasswordConfirmation)) return 2;          // Incorrect confirmation
    if(!currentPassword.equals(currentUser.getPassword())) return 3;    // Given current password doesn't match the one stored in the database for this user

    int resStatus = 0;
    EntityManager em  = factory.createEntityManager();
    try {
        em.getTransaction().begin();
            currentUser.setPassword(newPassword);
        em.getTransaction().commit(); // NO ERRORS at all...
    }
    catch(Exception e) {
        System.out.println(e.getMessage());
        resStatus = 4;  // Exception
    }
    finally {
      em.close();
    }

    return resStatus;
}

我也尝试过使用EntityManager.find(...),它应该从USER 表中返回同一行(确实如此),但结果还是一样——事务开始、完成、实体管理器关闭但表@987654331 @ 被假定更改的用户是相同的。

有什么想法吗?我在另一个项目中使用了相同的例程,但用于设置其他内容。那里的数据库是PostreSQL,我还没有遇到过这样的问题。在使用 SQLite 数据库时,我没有收到任何错误,但提交以某种方式失败。

【问题讨论】:

  • Eclipselink 是否提供对 SQLite 的全面支持?我知道它不习惯,而且每当我想使用该数据存储时,我都会使用 DataNucleus。查看 EclipseLink 日志可能会发现很多
  • 触发更改密码时,没有看到任何事务提交。另外,我在哪里可以找到一些关于 EclipseLink 在 SQLite 方面支持的信息。我试着四处寻找,但什么也没出现。仍然我真的怀疑它是否支持persist()(使用事务!)但不是简单地更改列中的值。我认为错误在我给出的代码中的其他地方。就是不知道在哪里。调试根本没有帮助,因为我没有收到任何错误。
  • 如果我搜索“Eclipselink 支持的数据库”,我会得到wiki.eclipse.org/EclipseLink/FAQ/…
  • 并查看当您想要更新其字段时“currentUser”处于什么状态。管理好了吗?分离?
  • 它将被分离,因为他使用 factory.createEntityManager() 获得了一个新的 EntityManager 用于事务。用户需要使用该 EntityManager 进行合并或读入

标签: sqlite jpa transactions eclipselink entitymanager


【解决方案1】:

我只是用hibernate等开发,但它是一样的,因为两者都实现了JPA。 如果您启动事务,JPA 将记住您在此事务中加载的所有实体,如果您更改某些内容,您将在 db 中看到更改(提交后)。

但在您的事务中,JPA 无法识别您的实体,因此更改不会持续存在。尝试在事务中加载实体。喜欢...

    em.getTransaction().begin();
        em.find(User.class, currentUser.getId()); //Reload the User from db, so it is attached to the session
        currentUser.setPassword(newPassword);
    em.getTransaction().commit();

有关方法的更多信息:

【讨论】:

  • Refresh 将引发异常,因为实例不受管理。需要合并或读入。
  • 如果您尝试更改数据库中不存在的用户的密码会很奇怪:P 但是您是对的,如果对象不在数据库中,刷新将是一个问题。如果在创建对象后刷新它,也可能会出现问题。所以db中不会有行。在这种情况下,您必须使用冲洗。对于这种情况,刷新或合并就可以了。或者您可以在修改对象后保存它,这将是第三种方法。
  • 对不起,我的意思不是如果行不存在刷新是一个问题,我的意思是如果你传入的实例不在当前上下文中管理,那么它是一个问题。刷新仅在此 EntityManager 管理的实例上有效,因此从任何其他 em 读取并传递的实体会导致异常。由于他们在读取 currentUser 后关闭上下文,并为更新获取不同的上下文,我建议您的解决方案需要更新以使用查找操作而不是刷新,或使用合并。
猜你喜欢
  • 2010-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2020-02-24
  • 2016-06-02
相关资源
最近更新 更多