Reputation: 252
I'm working on GAE with Spring framework 4.0.5 and Spring Data jpa 1.3.5. I' trying to retrieve a list of object in a OneToMany relation, but I receive the following error:
You have just attempted to access field "organizationMemberships" yet this field was not detached when you detached the object. Either dont access this field, or detach it when detaching the object.
The User Entity is:
@Entity
@Table(name="users")
@NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO )
private Long id;
private String email;
private String password;
private String salt;
private String slug;
private int status;
private String username;
//bi-directional one-to-one association to UserContact
@OneToOne(mappedBy="user")
private UserContact userContact;
//bi-directional one-to-one association to UserDetail
@OneToOne(mappedBy="user", cascade = CascadeType.ALL)
private UserDetail userDetail;
//bi-directional many-to-one association to UsersApisession
@OneToMany(mappedBy="user")
private List<UsersApisession> usersApisessions;
@OneToMany(mappedBy="user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<OrganizationMember> organizationMemberships;
...
}
While the OrganizationMember is:
@Entity
@Table(name="organization_members")
@NamedQuery(name="OrganizationMember.findAll", query="SELECT o FROM OrganizationMember o")
public class OrganizationMember implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO )
private Long id;
private String email;
private int status;
//bi-directional many-to-one association to Organization
@ManyToOne
private Organization organization;
//bi-directional many-to-one association to User @JoinColumn(name="member_id")
@ManyToOne(fetch = FetchType.LAZY)
private User user;
//bi-directional many-to-one association to OrganizationPosition
@ManyToOne
@JoinColumn(name="position_id")
private OrganizationPosition organizationPosition;
...
}
My userRepository is an interface according to Spring JPA:
@Transactional
public interface UserRepository extends JpaRepository<User, Long>{
User findByEmail(String email);
User findBySlug(String slug);
}
The code of my controller is the following:
@Controller
public class ProtectedSiteController {
...
@Autowired
private UserRepository userRepo;
@RequestMapping(method = RequestMethod.GET, value="/afterLogin")
public String afterLogin(HttpServletRequest request, HttpServletResponse response){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userRepo.findByEmail(auth.getName());
List<OrganizationMember> memberList = user.getOrganizationMemberships();
...
}
}
This is my configuration for transaction:
@Configuration
@EnableJpaRepositories("com.example.repository")
@EnableTransactionManagement
public class JpaApplicationConfig {
private static final Logger logger = Logger
.getLogger(JpaApplicationConfig.class.getName());
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
Map<String, String> map = new HashMap<String, String>();
map.put("datanucleus.NontransactionalRead","true");
map.put("datanucleus.NontransactionalWrite","false");
map.put("datanucleus.storeManagerType","rdbms");
map.put("datanucleus.autoCreateSchema" ,"false");
map.put("datanucleus.validateTables" ,"false");
map.put("datanucleus.validateConstraints" ,"false");
map.put("datanucleus.jpa.addClassTransformer" ,"true");
map.put("datanucleus.singletonEMFForName", "true");
LocalContainerEntityManagerFactoryBean lce= new LocalContainerEntityManagerFactoryBean();
lce.setPersistenceProviderClass(org.datanucleus.api.jpa.PersistenceProviderImpl.class);
DriverManagerDataSource dmds = new DriverManagerDataSource();
dmds.setDriverClassName("com.mysql.jdbc.Driver");
dmds.setUrl("jdbc:mysql://localhost:3306/example");
dmds.setUsername("example");
dmds.setPassword("example");
lce.setDataSource(dmds);
lce.setPackagesToScan("com.example.models");
lce.setJpaPropertyMap(map);
lce.setLoadTimeWeaver(new org.springframework.instrument.classloading.SimpleLoadTimeWeaver());
return lce;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
logger.info("Loading Transaction Manager...");
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(emf);
return txManager;
}
@Bean
public PersistenceAnnotationBeanPostProcessor postProcessor(){
return new PersistenceAnnotationBeanPostProcessor();
}
}
I've followed either the GAE tutorials either the Spring ones. What is my mistake? Thank you.
Upvotes: 1
Views: 176
Reputation: 8272
The fast trick is
put on method afterLogin(..) @Transactional
..
the problem is that when user returns, the transaction is closed and for this reason cannot retrieve Organization Membership (detached).
Another solution is change the fetch type in EAGER, @OneToMany
by default is LAZY.
@Service
public class ServiceClass {
...
@Autowired
private UserRepository userRepo;
@Transactional
public List<OrganizationMember> method(String name){
User user = userRepo.findByEmail(name);
return user.getOrganizationMemberships();
}
}
@Controller
public class ProtectedSiteController {
...
@Autowired
private ServiceClass serviceClass;
@RequestMapping(method = RequestMethod.GET, value="/afterLogin")
public String afterLogin(HttpServletRequest request, HttpServletResponse response){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<OrganizationMember> memberList = serviceClass.method(auth.getName());
...
}
}
I hope I've given you all the answers about your question.
Upvotes: 1