Hibernate 4 + Ressort des Données CrudRepository, CLI application: échec paresseusement initialiser une collection: impossible d'initialiser le proxy - pas de Session
Je suis en train de lancer de Printemps à base de CLI application qui utilise les Données du Printemps CrudRepository pour accéder à Hibernate4 base de la couche de persistance mis en œuvre en utilisant les annotations JPA sur MySQL5 (InnoDB) de la base de données à l'aide de c3p0 pool de connexion.
Je reçois l'exception suivante:
Exception in thread "main" java.lang.RuntimeException: org.springframework.orm.hibernate3.HibernateSystemException: failed to lazily initialize a collection of role: <package>.entity.User.categories, could not initialize proxy - no Session; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: <package>.entity.User.categories, could not initialize proxy - no Session
Je suis novice avec le Printemps des Données et de la mise en veille prolongée. De mon point de vue c'est un problème avec deux opérations distinctes (l'une dans UserServiceImpl::findByLogin
et un autre dans CategoryServiceImpl::deleteByUser
). Changer User
entité à utiliser Désireux type d'extraction pour les Catégories aide, mais je veux utiliser le chargement paresseux dans cette méthode.
Puis-je encore utiliser paresseux type d'extraction dans UserServiceImpl::findByLogin
et récupérer les objets dépendants service des consommateurs plus tard, avec CrudRepository
de Printemps et géré les opérations de Service de Couche?
Extrait de la demande qui provoque une exception:
User user = userService.findByLogin(login);
categoryService.deleteByUser(user);
EDIT: j'ai essayé d'utiliser EntityManager::merge
, mais avec pas de chance:
@Service
@Repository
@Transactional
public class CategoryServiceImpl implements CategoryService, InitializingBean {
@Autowired
private CategoryRepository repository;
@Autowired
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
@Override
public void afterPropertiesSet() throws Exception {
entityManager = entityManagerFactory.createEntityManager();
}
@Override
@Transactional(readOnly = true)
public Category findById(Long categoryId) {
return repository.findOne(categoryId);
}
@Override
@Transactional
public Category save(Category category) {
return repository.save(category);
}
@Override
@Transactional
public void delete(Category category) {
repository.delete(category);
}
@Override
@Transactional
public void deleteByUser(User user) {
entityManager.merge(user);
repository.delete(user.getCategories());
}
}
Services (injecté avec @Autowired
):
Le service à l'usager:
package <package>.service.jpa;
import com.google.common.collect.Lists;
import <package>.entity.User;
import <package>.repository.UserRepository;
import <package>.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Repository
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository repository;
@Override
@Transactional(readOnly = true)
public List<User> findAll() {
return Lists.newArrayList(repository.findAll());
}
@Override
@Transactional(readOnly = true)
public User findById(Long userId) {
return repository.findOne(userId);
}
@Override
@Transactional(readOnly = true)
public User findByLogin(String login) {
return repository.findByLogin(login);
}
@Override
@Transactional
public User save(User user) {
return repository.save(user);
}
@Override
@Transactional
public void delete(User user) {
repository.delete(user);
}
}
Catégorie de service:
package <package>.service.jpa;
import <package>.entity.Category;
import <package>.entity.User;
import <package>.repository.CategoryRepository;
import <package>.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Repository
@Transactional
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryRepository repository;
@Override
@Transactional(readOnly = true)
public Category findById(Long categoryId) {
return repository.findOne(categoryId);
}
@Override
@Transactional
public Category save(Category category) {
return repository.save(category);
}
@Override
@Transactional
public void delete(Category category) {
repository.delete(category);
}
@Override
@Transactional
public void deleteByUser(User user) {
repository.delete(user.getCategories());
}
}
Entités:
Utilisateur:
package <package>.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table
public class User {
private Long userId;
private int version;
private String login;
private Set<Category> categories = new HashSet<Category>();
private Set<CategoryFeed> categoryFeeds = new HashSet<CategoryFeed>();
@Id
@GeneratedValue
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Column
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<Category> getCategories() {
return categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
public void addCategory(Category category) {
categories.add(category);
}
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<CategoryFeed> getCategoryFeeds() {
return categoryFeeds;
}
public void setCategoryFeeds(Set<CategoryFeed> categoryFeeds) {
this.categoryFeeds = categoryFeeds;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User user = (User) o;
if (login != null ? !login.equals(user.login) : user.login != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
return login != null ? login.hashCode() : 0;
}
}
Catégorie:
package <package>.entity;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"userId", "title"}))
public class Category {
public static final String ROOT_CATEGORY_TITLE = "";
private Long categoryId;
private int version;
private User user;
private String title = ROOT_CATEGORY_TITLE;
private Set<CategoryFeed> feeds = new HashSet<CategoryFeed>();
@Id
@GeneratedValue
public Long getCategoryId() {
return categoryId;
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@ManyToOne
@JoinColumn(name = "userId")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Column(name = "title")
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@ManyToMany(mappedBy = "categories", cascade = CascadeType.ALL)
public Set<CategoryFeed> getFeeds() {
return feeds;
}
public void setFeeds(Set<CategoryFeed> feeds) {
this.feeds = feeds;
}
public void addFeed(CategoryFeed feed) {
this.feeds.add(feed);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Category)) {
return false;
}
Category category = (Category) o;
if (title != null ? !title.equals(category.title) : category.title != null) {
return false;
}
if (user != null ? !user.equals(category.user) : category.user != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = user != null ? user.hashCode() : 0;
result = 31 * result + (title != null ? title.hashCode() : 0);
return result;
}
}
Vous devez vous connecter pour publier un commentaire.
D'exécuter ce code:
sein d'une transaction unique, de cette façon:
qui est sûr de faire la même session Hibernate est utilisé pour les deux opérations.
deleteCategoriesByUser
méthode parce que j'ai déjà récupéré l'utilisateur. EtdeleteCategoriesByUser
méthode n'avez pas à connaître de tels détails de mise en œuvre à la corvée d'catégories de nouveau. Est-il mieux de la pratique ou de problème dans mon architecture?User user = userService.findByLogin(login); if (...) {categoryService.deleteByUser(user);}
mon CLI code de l'application dans la transaction? Mais de mon point de vue c'est une autre solution plutôt que de meilleures pratiques. Comme je comprends que je devrais en quelque sorte dire hibernate qu'il doit extraire de l'Utilisateur à l'aide de catégories courantes de connexion et de transaction. Mais je ne sais pas comment.Je ne pense pas que votre problème est de le faire avec la transaction, vous semblez avoir de l'installation de votre déclarative des limites des transactions correctement (par défaut Printemps transaction propagation est NÉCESSAIRE-à-dire si vous appelez la méthode imbriquée décorées avec
@Transactional
, pas de chevauchement de transaction est créée)Votre problème semble être causé par le
categories
propriété de l'utilisateur de l'objet n'est pas remplie lorsque vous êtes aller chercher (parce que vous le définissez paresseux) ET/OU mise en veille prolongée incapable de la remplir avant la fermeture de la session surdeleteByLogin
-- d'où l'objet est déjà détaché.Comme fas comme je peux le voir il y a deux méthode pour résoudre ce problème:
1. Avec impatience de récupérer les catégories de propriété d'un utilisateur
Marque
categories
propriété avec impatience récupérés:@OneToMany(fetch = FetchType.EAGER, ...)
(AVERTISSEMENT: peut manger une quantité massive de la mémoire), ou de l'utilisationLEFT JOIN FETCH
de syntaxe lors de l'interrogation d'un utilisateur, de sorte que soncategories
propriété est renseignée2. Fusionner les détachée de l'utilisateur sur
deleteByLogin
Sur
deleteByLogin
, fusion de l'utilisateur de l'objet de la première, dans le contexte de persistance de sorte que lecategories
propriété peut être paresseusement chargé chaque fois que vous appelezgetCategories()
categories
sont détachés parfindByLogin
? Parce que la frontière de la transaction se termine à la fin de la méthode? 1. Je veux utiliser paresseux associations pour des raisons de performances.findByLogin
est une méthode usage général, et dans la plupart des cas, les associations ne seront pas utilisés. 2. Je suis en utilisant la norme JPA annotations. Est-il possible d'utiliser hibernate-classes spécifiques? Et comment puis-je recevoir de l'instance de session dansCrudRepository
?em.merge(objname)
où em est l'EntityManager de référence.@Transactional
annotation traitée comme une couche de la vue? Est-il vrai même dans l'interface de ligne de l'application?@Transactional
annotationmain
méthode de CLI app). Sont objets de l'entité est détaché, avant de revenir à la Manette?@Transactional
retourne, le gestionnaire de transactions sera la clôture de la transaction et de détacher les objets. Veuillez en lire plus sur hibernate manuel de référence: docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch11.html