问题
How should I manage the create, edit and delete operations with entites as simple as possible?
For example:
User:
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);
}
...
}
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);
}
...
}
Item:
@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;
}
...
}
Am I using the jpa annotations right?
What should I write here?
EntityManager em = getEntityManager();
em.getTransaction().begin();
???
em.getTransaction().commit();
Do I have to just call the em.remove/persist/merge methods for delete/create/edit operations?
And when should I use the javax.persistence.EntityManager.getReference method in these operations?
回答1:
Find() delivers the entity from the cache of the persistence context or if he is not there, it will be loaded from the database.
GetReference() does not load the entity immediately. A proxy( a certain object, a so called "deputy" with enriched methods for loading the actual entity) is returned. So it is a realisation with help of LazyLoading.
Only if the attributes of the proxy or other persistence methods are needed/called the proxy interacts and loads the actual entity from the database.
Eg:
User us = em.find(User.class, 70992);
GetReference() is used similarly.
User us = em.getReference(User.class, 70922);
If the entity for the user id is not known in the persistence context, an EntityNotFoundException() is thrown.
I usually use getReference method when i do not need to access database state (I mean getter method). Just to change state (I mean setter method).
In above case if I want to update age of user like below after getting user:
setAge(age);
If i call find method, JPA provider, behind the scenes, will call
SELECT NAME, AGE FROM USER WHERE USER_ID = ?
UPDATE USER SET AGE = ? WHERE USER_ID = ?
If i call getReference method, JPA provider, behind the scenes, will call
UPDATE PERSON SET AGE = ? WHERE USER_ID = ?
Because when you call getReference, you will get a proxy object.
For rest we have to use remove, persist and merge like you have said
回答2:
Personally I recommend reading about the Repository Software Pattern and the Single Responsability Principle.
My idea would be to create, for instance, a UserRepository and another class, like a Controller, would go to that repository after creating the necessary objects and try to persist them.
Should work like this:
First get the entity manager and the transaction as you did, then try to persist()
the entities. If the persist()
method detects that the entity is in the persistence, it will throw a PersistenceException
. Catch it, obtain a new transaction and call the merge()
method.
回答3:
The easiest way is not the best way. The best easiest way is this one, I think (but concerning update actions, you better read more about 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.
}
}
P.S. Do not forget to lock records from database when updating (using pessimistic lock or the catching an optimistic lock exception).
You can also find lots about it if you search for "CRUD Operations with JPA". First tutorial I found is this one: http://www.objectdb.com/java/jpa/persistence/crud
回答4:
Am I using the jpa annotations right?
At first glance, I'd only question the absence of a @Version field, which is necessary for the EntityManager to perform optimistic locking, which is the standard safeguard against lost updates unless you use a serializable isolation level (which would require special configuration on many database systems).
Do I have to just call the em.remove/persist/merge methods for delete/create/edit operations?
That is the easiest and usually best way.
And when should I use the javax.persistence.EntityManager.getReference method in these operations?
getReference is useful if you want to refer to an entity without loading its data from the database. For instance, you could have:
public void updateUser(Item item, int userId) {
item.setUser(entityManager.getReference(User.class, userId)); // does not require a query
entityManager.merge(item);
}
Transactions
You'll want to rollback the transaction if an exception is thrown and close the entityManager once you are done with it (both in case of success and failure).
On a related note, if your application is not tiny, you'll want to look into declarative transaction management so you don't have to code them for every operation. For instance, in Spring you'd simply annotate the class with @Transactional to have Spring start (and end) a transaction whenever a method is invoked.
来源:https://stackoverflow.com/questions/37800546/jpa-create-edit-and-delete-entities-from-database