【问题标题】:JPA create, edit and delete entities from databaseJPA 从数据库中创建、编辑和删除实体
【发布时间】:2016-06-14 08:34:55
【问题描述】:

我应该如何尽可能简单地管理实体的创建、编辑和删除操作?

例如:

用户:

public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    ...

    // Item can't exist without user
    @OneToMany(cascade=CascadeType.ALL,mappedBy = "user",orphanRemoval=true)
    private Set<Item> items = new HashSet<Item>();

    public Set<Item> getItems() { return items; }

    public void addItem(Item item) {
        items.add(item);
    }

    public void removeItem(Item item) {
        if(!items.contains(item)) return;
        items.remove(item);
    }

    // Group can exist without a user
    @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH},mappedBy="users")
    private Set<Group> groups = new HashSet<Group>();

    public Set<Group> getGroups() { return groups; }

    public void setGroups(Set<Group> groups) { this.groups = groups; }

    public void addGroup(Group group) {
        groups.add(group);
    }

    publi void removeGroup(Group group) {
        if(!groups.contains(group)) return;
        groups.remove(group);
    }

    ...
}

组:

@Entity
public class Group {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    ...

    @ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinTable(name = "GroupToUser", joinColumns =
    @JoinColumn(name = "GroupId", referencedColumnName="Id"), inverseJoinColumns =
    @JoinColumn(name = "UserId", referencedColumnName="Id"))
    private Set<User> users = new HashSet<User>();

    public Set<User> getUsers() { return users; }

    public void setUsers(Set<User> users) { this.users = users; }

    public void addUser(User user) {
        user.addGroup(this);
    }

    publi void removeUser(User user) {
        if(!users.contains(user)) return;
        users.remove(user);
    }

    ...
}

项目:

@Entity
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    ...

    @ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
    @JoinColumn(name="UserId")
    private User user;

    public Set<User> getUser() { return user; }

    public void setUser(User user) {
        this.user = user;
    }

    publi void removeUser() {
        this.user = null;
    }

    ...
}

我使用 jpa 注释对吗?

我应该在这里写什么?

EntityManager em = getEntityManager();
em.getTransaction().begin();
???
em.getTransaction().commit();

我是否只需要调用 em.remove/persist/merge 方法来进行删除/创建/编辑操作?

什么时候应该在这些操作中使用 javax.persistence.EntityManager.getReference 方法?

【问题讨论】:

  • 您似乎明白EntityManager 实例是用来管理JPA 实体的。你试过测试吗?

标签: java jpa eclipselink jpa-2.1


【解决方案1】:

Find() 从持久化上下文的缓存中传递实体,或者如果他不在那里,它将从数据库中加载。

GetReference() 不会立即加载实体。返回一个代理(某个对象,一个所谓的“代理”,具有用于加载实际实体的丰富方法)。所以这是借助 LazyLoading 实现的。

仅当需要/调用代理的属性或其他持久性方法时,代理才会交互并从数据库加载实际实体。

例如:

User us = em.find(User.class, 70992);

GetReference() 的用法类似。

 User us = em.getReference(User.class, 70922);

如果用户 id 的实体在持久性上下文中未知,则抛出 EntityNotFoundException()。

当我不需要访问数据库状态时,我通常使用 getReference 方法(我的意思是 getter 方法)。只是为了改变状态(我的意思是setter方法)。

在上述情况下,如果我想在获取用户后更新用户的年龄,如下所示:

setAge(age);

如果我调用 find 方法,JPA 提供者会在幕后调用

SELECT NAME, AGE FROM USER WHERE USER_ID = ?

UPDATE USER SET AGE = ? WHERE USER_ID = ?

如果我调用 getReference 方法,JPA 提供者会在后台调用

UPDATE PERSON SET AGE = ? WHERE USER_ID = ?

因为当你调用getReference的时候,你会得到一个代理对象。

为了休息,我们必须像你说的那样使用删除、持久化和合并

【讨论】:

    【解决方案2】:

    我个人建议阅读Repository Software PatternSingle Responsability Principle

    例如,我的想法是创建一个 UserRepository,而另一个类(如 Controller)会在创建必要的对象后转到该存储库并尝试持久化它们。

    应该像这样工作:

    首先像你一样获取实体管理器和事务,然后尝试persist()实体。如果persist()方法检测到实体在持久化中,会抛出PersistenceException。捕获它,获取一个新事务并调用merge() 方法。

    【讨论】:

    • 如果你不知道它是否是新的,为什么不直接使用合并,让 JPA 处理插入或更新?
    【解决方案3】:

    最简单的方法不是最好的方法。我认为最简单的方法是这个(但关于更新操作,你最好阅读更多关于 JPQL namedQueries / orm.xml):

    @WebFilter("*.htm")
    public class JPAFilter implements Filter {
        private static final EntityManagerFactory entityManagerFactory
            = Persistence.createEntityManagerFactory(/* yourprojectname */);
        private static final ThreadLocal<EntityManager> entityManagers
            = new ThreadLocal<>();
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {}
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
                entityManagers.set(entityManagerFactory.createEntityManager());
            EntityManager entityManager = entityManagers.get();
            try {
                request.setCharacterEncoding("UTF-8");
                chain.doFilter(request, response);
            } finally {
                if (entityManager.getTransaction().isActive()) {
                    entityManager.getTransaction().rollback();
                }
                entityManager.close();
                entityManagers.remove();
            }
        }
    
        public static EntityManager getEntityManager() {
            return entityManagers.get();
        }
    
        @Override
        public void destroy() {
            entityManagerFactory.close();
        }
    }
    
    ----
    
    abstract class AbstractDAO {
        protected EntityManager getEntityManager() {
            return JPAFilter.getEntityManager()
        }
    }
    
    ----
    
    public class UserDAO extends AbstractDAO {
        public void create(User user) {
            getEntityManager().persist(user);
        }
    
        public User read(long id) {
            return getEntityManager().find(User.class, id);
        }
    
        public void delete(long id) {
            if (user != null) {
                getEntityManager().remove(user);
            }
        }
    }
    
    ----
    
    abstract class AbstractService {
        private EntityManager getEntityManager() {
            return JPAFilter.getEntityManager();
        }
    
        protected void beginTransaction() {
            getEntityManager().getTransaction().begin();
        }
    
        protected void commit() {
            getEntityManager().getTransaction().commit();
        }
    
        protected void rollback() {
            getEntityManager().getTransaction().rollback();
        }
    }
    
    ----
    
    public class UserService extends AbstractService {
        private final UserDAO userDAO = new UserDAO();
    
        public void create(User user) {
            beginTransaction();
            userDAO.create(user);
            commit();
        }
    
        public User read(long id) {
            return userDAO.read(id)
        }
    
        public void delete(long id) {
            userDAO.delete(id);
        }
    }
    
    ----
    
    @WebServlet("/users.htm")
    public class ManageUsersServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
        private static final String VIEW = "/WEB-INF/JSP/users.jsp";
        private final transient UserService userService = new UserService();
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // here you do whatever you want using your userService methods.
        }
    }
    

    附:更新时不要忘记锁定数据库中的记录(使用悲观锁或捕获乐观锁异常)。

    如果您搜索“使用 JPA 进行 CRUD 操作”,您也可以找到很多相关信息。我找到的第一个教程是这个:http://www.objectdb.com/java/jpa/persistence/crud

    【讨论】:

      【解决方案4】:

      我使用的 jpa 注释对吗?

      乍一看,我只是质疑缺少 @Version 字段,这是 EntityManager 执行乐观锁定所必需的,这是防止更新丢失的标准保护措施,除非您使用可序列化的隔离级别(这需要许多数据库系统上的特殊配置)。

      我是否只需要调用 em.remove/persist/merge 方法来进行删除/创建/编辑操作?

      这是最简单,通常也是最好的方法。

      什么时候应该在这些操作中使用 javax.persistence.EntityManager.getReference 方法?

      如果你想引用一个实体而不从数据库加载它的数据,getReference 很有用。例如,您可以:

      public void updateUser(Item item, int userId) {
          item.setUser(entityManager.getReference(User.class, userId)); // does not require a query
          entityManager.merge(item);
      }
      

      交易

      如果抛出异常,您需要回滚事务,并在完成后关闭 entityManager(无论是成功还是失败)。

      在相关说明中,如果您的应用程序不是很小,您将需要研究声明式事务管理,这样您就不必为每个操作编写代码。例如,在 Spring 中,您只需使用 @Transactional 注释类,以便在调用方法时让 Spring 开始(和结束)事务。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-29
        • 1970-01-01
        • 1970-01-01
        • 2015-03-28
        相关资源
        最近更新 更多