JPA/Hibernate bidirectional many-to-one results in StackOverflowException - java

I have entities User and GrantedRole that have a bidirectional one-to-many relation.
When I try to add a GrantedRole to the Set in User, there is no exception thrown, but when I debug the variables for the User and GrantedRole object have a description that reads
com.sun.jdi.InvocationException occurred invoking method.
The different fields for the variables can be read while debugging, but when I select the roles field in User or the user field in GrantedRole I get the same description as above.
When I go into the Set of GrantedRole in user, I eventually find the following description:
Detail formatter error:
An exception occurred: java.lang.StackOverflowError
My code:
public class User {
private Set<GrantedRole> roles = new HashSet<GrantedRole>();
public User() {
super();
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
public Set<GrantedRole> getRoles() {
return roles;
}
public void setRoles(Set<GrantedRole> roles) {
this.roles = roles;
}
// equals and hashCode are based on username
// toString is based on all fields
}
public class GrantedRole {
private user user;
public GrantedRole() {
super();
}
public GrantedRole(User user, Role role, Organization organization) {
this.user = user;
this.role = role;
this.organization = organization;
}
#ManyToOne
#NotNull
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
// equals and hashCode are based on all fields
// toString was based on all fields, I've changed it to not include User.
}
public class Test {
public void testStuff() {
User user = new User("name");
GrantedRole role = new GrantedRole(user, "role"); //this sets the user field
user.getRoles().add(role); //doesn't throw Exception, but debugging shows InvocationTargetException and StackOverflowException
System.out.println(user.getRoles()); //throws StackOverflowException, as I would expect
}
}
It was my understanding that I should be able to set up a bidirectional relation in this way. I read multiple tutorials, like this one, and I don´t see what I am doing differently to cause the .add(role) to go wrong.
I left out some trivial code, if it is needed I will gladly provide it. I haven't made code to ensure that a GrantedRole with a reference to a User is referenced by that user in return, but I think that is not relevant for the problem I'm having.

So, I was just being stupid and had a recursive call in the toString() methods.
User.toString() tried to print the roles, and GrantedRole.toString() tried to print the user.
I fixed it by altering the GrantedRole.toString() to print user.getUsername(), thus breaking the cycle.

Related

Does SDN4 allow multiple relationship of collection type to the same Entity?

When I try to get different relationship type of collection object, the collection property will retrieve all the same entity type even if the relationship type if not the collection relationship type. Is it a bug ?
The entity Demo contains two fields which reference to User entity : user and users
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import org.neo4j.ogm.annotation.RelationshipEntity;
#NodeEntity
public class Demo extends FormLog implements java.io.Serializable {
private String name;
#Relationship(type="MY_USER")
private User user;
#Relationship(type="DEMO_USERS")
private Set<User> users = new HashSet<User>();
public Demo(){}
public Demo(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
When I save the demo with 1 "MY_USER" and 2 "DEMO_USERS", it is fine.
But when I get find the Demo by group id, the "DEMO_USERS" return 3 users.
#Test
public void test_saveAndFindOne_save2KindsOfUser_NoConfliction(){
Demo demo = new Demo();
List<User> user = userService.findUserByNameLike("Hank");
demo.setUser(user.get(0));
demo.getUsers().add(user.get(1));
demo.getUsers().add(user.get(2));
demo.setName("Set Multiple");
demo = demoRepository.save(demo);
System.out.println("Users size = "+ demo.getUsers().size());
System.out.println("==========Get Demo from DB ==============");
Demo db = demoRepository.findOne(demo.getId());
System.out.println("Users size = "+ db.getUsers().size());
}
The output
Users size = 2
==========Get Demo from DB ==============
Users size = 3
This is probably what you are running into: https://github.com/neo4j/neo4j-ogm/issues/38
You should be able to work around this by annotating your setUser and setUsers methods with the appropriate #Relationship
If that does not help, please report an issue (or add onto the one above).
Update:
Confirmed that this is indeed the same issue as described in https://github.com/neo4j/neo4j-ogm/issues/38
Until this issue is fixed, you'll need to make your objects navigable in both directions. In your example, User will need to have two references to Demo each annotated with the appropriate #Relationship

How to use mongodb embedded schema in java

I am stuck here, i want to keep address of user as embedded document . But there,it is creating another table of address in database. so i want one table "user" and inside it,i want my embedded document like "address". what will be the domain design of user ?.
Now,my user domain is
class User{
String name;
Address adress;
}
My address domain is
class Address{
String city;
}
My controller is
#RestController
#RequestMapping("/api/v1")
public class UserController {
public Map<String, Object> saveUser(#RequestBody User user) {
userService.saveUser(user);
return ResponseHandler.generateResponse("User registerted successfully", HttpStatus.ACCEPTED, false, null);
}
}
My service is
public void saveUser(User user){
Address a = new Address();
a.setCity("delhi");
User user = new User();
user.setAddress(a);
userDao.save(user);
}
I want result is in database,not table of address separately.i am using spring-data-mongotemplate for this.how can i do this ?
user{
address:[{},{}] // multiple address could be
}
Maybe something like this?
#Document
public class User {
private String name;
private List<Address> addresses = new ArrayList<>();
// ctr, getters, setters...
public static class Address {
private String city;
public Address(String city) {
this.city = city;
}
// getters, setters...
}
}
In controller:
User.Address address = new User.Address("NY");
user.setAddress(address);
mongoTemplate.save(user);
Basically, parsing User from json should work:
{ name : "userName", addresses : [ { city : 'NY' } ] }
And you don't have to embed Address class into User class. Just add #Document on User and ebmed Address object in it.
Use #Document annotation in user, and both must implements Serializable.
Regards.

Hibernate Model: set default #ManyToOne value of a Child Class

I have class User which has #ManyToOne field Role.
#Entity
#Table(name="USERS")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorValue("ROLE_ADMIN")
#DiscriminatorColumn (name="ROLENAME", discriminatorType= DiscriminatorType.STRING, length=20)
public class User extends BaseEntity implements UserDetails {
// all others fields like username, password,...
#ManyToOne
#JoinColumn(name = "role_id", referencedColumnName="id")
#NotNull(message = "ROLE field is mandatory")
private Role role;
//getter and setter
}
I have many classes that extends User: UserSeller, UserClient, UserOffice....
#Entity
#DiscriminatorValue("ROLE_SELLER")
#AttributeOverride(name = "role", column = #Column(name = "role_id"))
public class UserSeller extends User {
//additional fields like CompanyId,...
//getter & setter
}
I have one panel where i can insert / edit / delete all kind of users,
but i must have also "n" panel: one for each kind of user.
When i'm in these panel, i would like to be able to insert these UserSeller without having
to put a where to choose role, but i want set this role as default.
• I tried to put a costructor in the UserSeller
#Entity
#DiscriminatorValue("ROLE_SELLER")
#AttributeOverride(name = "role", column = #Column(name = "role_id"))
public class UserSeller extends User {
#Transient
RoleService roleService;
#Transient
User user = new User();
public UserSeller()
{
super();
try
{
this.setRole(roleService.getRole("ROLE_SELLER"));
}
catch (RecordNotFoundException e)
{
}
}
but i get this error:
Could not get constructor for org.hibernate.persister.entity.SingleTableEntityPersister
• i tried to pass a User object to constructor:
public UserSeller(User user)
and in controller i do this:
User user = new User();
UserSeller seller = new UserSeller(user);
model.addAttribute("seller", seller);
but i get this error:
No default constructor for entity: com.machinet.model.UserVenditore
If, in UserSeller i declare again the Role field i get error about
"Redefined Column"....
Finally i found this that i though could be my solution (in
UserSeller class):
#PrePersist
public void prePersist() {
try
{
this.setRole(roleService.getRole("ROLE_SELLER"));
}
catch (RecordNotFoundException e)
{
}
}
but when i'm in the UserSeller panel and i try to add a new Seller, it doesnt' take the default role and the validation fails.
I would like to know how can i do the trick: i want that my UserSeller, UserClient,... have a default value when i insert a new record.
Do i really need to do this in the controller? is this the only way? because for a beginner like me, it doesn't look so so elegant solution:
UserVenditore venditore = new UserVenditore();
try
{
venditore.setRole(roleService.getRole("ROLE_VENDITORE"));
}
catch (RecordNotFoundException ex)
{
}
model.addAttribute("venditore", venditore);
edit: also this last solution doesn't work: it fails the validation!
Thank you for any suggestion!
for now, i found only this solution (in the ControllerClass):
in removed #Valid before #ModelAttribute
i manually set the role
i manually validate the object
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addingSeller(#ModelAttribute UserSeller seller,
BindingResult result, RedirectAttributes redirectAttrs) {
logger.info("IN: Seller/add-POST");
//set role
try
{
seller.setRole(roleService.getRole("ROLE_SELLER"));
}
catch (RecordNotFoundException ex)
{
}
//validation
validator.validate(seller, result);
if (result.hasErrors()) {
logger.info("Seller-add error: " + result.toString());
redirectAttrs.addFlashAttribute("org.springframework.validation.BindingResult.seller", result);
redirectAttrs.addFlashAttribute("seller", seller);
} else {
try
{
SellerService.addSeller(seller);
//message to the user
String message = "Seller added!";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-success");
}
catch (DuplicateRecordException e)
{
//the username already exists
//message to the user
String message = "Already exists an user with this USERNAME";
redirectAttrs.addFlashAttribute("message", message);
redirectAttrs.addFlashAttribute("message_class", "alert-danger");
redirectAttrs.addFlashAttribute("seller", seller);
}
}
return "redirect:/sellers/list";
}

Is it Logical Creating Separate Java Class for Database Table?

I have 5 different tables in my database on MS SQL Server 2012. I have created class for my UserTable and filled in setters and getters shown below , is it logical to put other tables's setters and getters inside the same glass or create separate class for other tables with setters and getters.
import java.math.BigDecimal;
import java.sql.Connection;
public class UserFR17setget {
Connection cn;
BigDecimal userID;
String UserName;
String UserPassword;
int UserSecurity;
BigDecimal ProjectID;
public BigDecimal getUserID() {
return userID;
}
public void setUserID(BigDecimal userID) {
this.userID = userID;
}
public String getUserName() {
return UserName;
}
public void setUserName(String UserName) {
this.UserName = UserName;
}
public String getUserPassword() {
return UserPassword;
}
public void setUserPassword(String UserPassword) {
this.UserPassword = UserPassword;
}
public int getUserSecurity() {
return UserSecurity;
}
public void setUserSecurity(int UserSecurity) {
this.UserSecurity = UserSecurity;
}
public BigDecimal getProjectID() {
return ProjectID;
}
public void setProjectID(BigDecimal ProjectID) {
this.ProjectID = ProjectID;
}
}
Regards
You're basically implementing a part of JPA yourself. In JPA a class maps to a database table, and you think in terms of entities instead of database tables. You might want to explore that later on (unless you wish to do so now).
If you just want to map database data into objects, you have a few reasonable choices. Do as you're doing now, make each database table have a related class (User is a lot better name for a class than UserFR17getset by the way), then you can manipulate the data as objects. A more higher level approach is to have the classes reference to other tables, so your User class would contain an Address reference (assuming there's a table Address), and when loading a User, it would also load the Address. This is similar to how JPA works, and then you would be able to do a single "load user 1" instead of "load user 1, load address for user 1". Of course you would be responsible for implementing the actual loading (unlike with JPA, where there's a lot of automation going on).
Don't make the Connection object a part of your class though, it has no business being there. You should use the Connection object with your loading method, such as User u = loadUser(connection, userId);

Models not being saved in Play Framework

Assume a model named User:
#Entity
public class User extends Model {
#Id
#Constraints.Min(10)
public Long id;
#Constraints.Required
public String username;
#Constraints.Required
public String password;
public static Finder<Long, User> find = new Finder<Long, User>(
Long.class, User.class
);
}
When I attempt to update an instance of User in my controller:
User user = User.find.where().eq("username", username).findUnique();
if(user != null) {
user.username = "some_new_username";
user.save();
}
no changes seem to be committed. I read somewhere that when you alter a model instance by its property, it does not get dirty and therefore no changes take place. Hence you should use a setter instead. In the documentation of Play Framework it is said that those setters (and getters) are generated automatically, but using user.setUsername(username) gives me a compilation error:
cannot find symbol [symbol: method setUsername(java.lang.String)] [location: class models.User]
Am I missing something?
Have you tried adding custom setters?
#Entity
public class User extends Model {
#Id
#Constraints.Min(10)
public Long id;
#Constraints.Required
public String username;
public void setUsername(String _username) {
username = _username;
}
#Constraints.Required
public String password;
public void setPassword(String _password) {
password = _password;
}
public static Finder<Long, User> find = new Finder<Long, User>(
Long.class, User.class
);
}
As far as I can tell, automatic getter/setter translation is broken in Play2. Your assignment:
user.username = "some_new_username";
should have triggered the function call:
user.setUsername("some_new_username");
This translation seems to be broken in Play 2. Here's my own question on the subject.

Categories

Resources