I have a Role node that contains some privileges that I am trying to persist into Neo4j. When I construct the object, I see that the privileges exist, but after the save call they disappear.
Here's my Role Node:
#NodeEntity
public class Role {
#GraphId Long id;
private RoleType roleType;
//#RelatedToVia(type="HAS_ROLE", direction=Direction.OUTGOING)
private List<Person> users;
private List<Privilege> defaultPrivileges;
//private List<Task> tasks;
public Role(){}
public Role(RoleType roleType){
this.roleType=roleType;
this.defaultPrivileges=roleType.getDefaultPrivileges();
}
}
Here's my save:
admin= roleRepository.save(admin);
Before I save the object it is fully populate and after it's empty. Any ideas as to why that might be
EDIT:
The code causing the issue is in my Privilege class.
This does not work:
public class Privilege {
private String name;
public Privilege(PrivilegeType pt) {
this.name = pt.name();
}
}
This works:
public class Privilege {
private String name;
public Privilege(String pt) {
this.name = pt;
}
}
Why would that be causing it to not persist? What am I missing in my RoleRepository?
It reloads the entity after storing it, and by default it only loads a shallow copy of related information.
you can use template.fetch(role.users) or template.fetch(role.tasks)
or add #Fetch to the tasks list for instance.
Related
i have problem with saving data in DB.I'm new in Spring Boot. When i run my program the result of writen data is: packagename#randomcode example:com.abc.patient.Patient#6e3e681e
This is my Entity class - Patient.java
#Entity
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
// getter, setter, constructor, etc
}
This is my CrudRepo PatientRepository.java
public interface PatientRepository extends CrudRepository<Patient,Integer> {
}
This is my Service class PatientService.java
#Service
public class PatientService {
#Autowired
private PatientRepository patientRepository;
public void savePatient (String name) {
Patient patient = new Patient(name);
patientRepository.save(patient);
}
public Optional<Patient> showPatient(int id) {
return patientRepository.findById(id);
}
public List<Patient> showAllPatients() {
List<Patient> patients = new ArrayList<>();
patientRepository.findAll().forEach(patients::add);
return patients;
}
}
I think that problem in in the savePatient method in this line:
Patient patients = new Patient(name);
I checked the "name" parameter and it's in 100% correct String. I'm using Derby DB.
The only problem you have is how you are printing out your Patient class. Define a proper toString() or just debug yourself to see the resulting fields. There is no problem in your JPA implementation.
See this question for the details of default toString
Try:
public void savePatient(Patient patient) {
patientRepository.save(patient);
}
I have two immutable classes: User and Department, they are connected using a bidirectional association - User has a reference to Department and Department has a list of Users. How to create a new Department instance with the provided Users?
Code:
class User {
private final Department department;
private final String name;
public User(Department department, String name) {
this.department = department;
this.name = name;
}
}
class Department {
private final List<User> users;
private final String name;
public Department(List<User> users, String name) {
this.users = new ArrayList<>(users);
this.name = name;
}
}
I feel in you case you can slightly modify your design and use special UsersBuilder, i.e.
class Department {
final List<User> users;
final String name;
public Department(String name) {
this.users = UsersBuilder.buildUsers(this);
this.name = name;
}
}
class UsersBuilder {
public static List<User> buildUsers(Department department) {
List<User> usersList = new ArrayList<>();
// add users to the list via department reference
return Collections.unmodifiableList(usersList);
}
}
In general, it is not really good idea to use object's reference before its constructor finishes; but in this particular case it looks safe.
In this case these objects will be really immutable.
You can produce immutable Departments and Users with an extra constructor on Department. From the questions' code, it is inferred that
A User object is just an association between a String and a Department
User references can't exist without a Department reference.
Since Users are truly just Strings associated to a Department, a Department can be constructed with a List<String> that represents all User names to be included and use that List<String> to create a List<User> within the Department constructor.
Note: what #andremoniy said about letting this escape from a constructor should not be made a habit of, but it is safe in this case since it is only being passed to a User instance's constructor where that User instance can't be accessed before the Department constructor returns.
Here's what it would look like, in Java 8:
public final class User {
private final Department department;
private final String name;
public User(Department department, String name) {
this.department = department;
this.name = name;
}
public Department getDepartment() {
return department;
}
public String getName() {
return name;
}
}
public final class Department {
private final List<User> users;
private final String name;
///Reversed argument list to avoid collision after erasure
public Department(String name, List<String> users) {
this.users = Collections.unmodifiableList(users.stream()
.map((s) -> new User(this,s)).collect(Collectors.toList()));
this.name = name;
}
public Department(List<User> users, String name) {
this.users = Collections.unmodifiableList(users);
this.name = name;
}
public List<User> getUsers() {
return users;
}
public String getName() {
return name;
}
}
One issue this solution has is that once a Department instance is created, it can be added to new instances of User without the constraint that a new instance of Department be created with an updated List. Consider other abstractions or creational patterns (a full blown Builder implementation where all constructors are private would be a good match here) if you need to support the addition/deletion of users from a Department while maintaining immutability.
Instantiate Department with empty list of users. Then use the Department to instantiate User and add the user instance to the Department's users list.
One approach is to slightly alter what you understand immutable to mean. In object oriented design it is conventional to distinguish between the attributes of an object and its associations. Associated objects are different entities to which the object has references. If you relax the definition of immutable to mean that the attributes of the object do not change, but allow the associations to change, you avoid this kind of problem.
In your case, User and Department objects would be associated with each other, and each would have a name attribute.
I think this is a matter of modeling as well. This is ok to think that an User has a Department and a Department have Users, but the question is how deep can you look into data from User and Department ends?
Does it make sense unless conceptually you to access user.department.user[2].name? What about department.user[10].addresses[1].street?
I really don't think so on most scenarios. It's a matter of information domain. You have bondaries while accessing data and this can also be expressed somehow into your models.
If Object Modeling kind represents the real world, this is ok to think that when you go to a department, you will see dozens of people working there and most likely all you will be able to know about them is the counting and the their names perhaps. So what slices of data you should be able to see from your object?
My approach for this is:
interface PersonInfo {
String name();
String lastName();
default fullName() { return name() + " " + lastName(); }
static PersonInfoBuilder personInfo() { return new PersonInfoBuilder(); }
static class PersonInfoBuilder {
...
}
}
interface Person extends PersonInfo {
DepartmentInfo department();
Set<Address> addresses();
//...
}
interface DepartmentInfo {
String name();
String building();
// builder ...
}
interface Department extends DepartmentInfo {
Set<PersonInfo> employees();
// ...
}
I don't think i'd need to show how the builders would work since if you noticed, for this scenario, the bidirectional nature of relationship is never there. So when you build a Person, all you need is the DepartmentInfo (department no employees not required), and the same is valid when you build a Department, when all you need to have is the PersonInfo from department's employees.
That's my way to think this problem conceptually. Any comments?
My solution is to: split one of the immutable classes into two classes: a class with the attributes and a class with the bidirectional association:
class Department {
private final String name;
public Department(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class User {
private final Department department;
private final String name;
public User(Department department, String name) {
this.department = department;
this.name = name;
}
}
class DepartmentWithUsers {
private final List<User> users;
private final Department department;
public DepartmentWithUsers(Department department, List<User> users) {
this.department = department;
this.users = new ArrayList<>(users);
}
}
So to create a new user and a department instance you have to:
create a new Department instance
create a new User instance and pass the created Department instance
create a new DepartmentWithUsers instance and pass the created User instance
I am using a typical User -> Role relationship with my application. I am taking advantage of Neo4j's ability to add metadata on the relationship through the RelationshipEntity annotation. For some unexplained reason the Role's properties are being 'nulled' out when I persists my relationship.
Here's my User
public class Person {
#GraphId Long id;
private String name;
#RelatedToVia(type="HAS_ROLE")
#Fetch
private Set<UserRoleRelationship> roles = new HashSet<UserRoleRelationship>();
public Person() {}
public Person(String name) { this.name = name; }
}
Role
#NodeEntity
public class Role {
#GraphId Long id;
private RoleType roleType;
#SuppressWarnings("unused")
private String roleName;
private #Fetch Set<Privilege> defaultPrivileges;
}
RelationshipEntity
#RelationshipEntity(type="HAS_ROLE")
public class UserRoleRelationship {
#GraphId Long id;
#StartNode private Person person;
#EndNode private Role role;
private List<Privilege> privileges = new ArrayList<Privilege>();
}
RoleType (Used in Role)
public enum RoleType {
ADMIN("Administrator", Arrays.asList(new Privilege(PrivilegeType.PRIV1.name()),
new Privilege(PrivilegeType.PRIV2.name()),
new Privilege(PrivilegeType.PRIV3.name()),
new Privilege(PrivilegeType.PRIV4.name()))),
USER("User", Arrays.asList(new Privilege(PrivilegeType.PRIV1.name()))),
private String name;
private Set<Privilege> defaultPrivileges = new HashSet<Privilege>();
private RoleType(String name, List<Privilege> privileges){
this.name=name;
this.defaultPrivileges=new HashSet<Privilege>(privileges);
}
public String getName() {
return name;
}
public Set<Privilege> getDefaultPrivileges() {
return defaultPrivileges;
}
}
When populating the relationship with a User and Role, all the data is set. However when I persists it the Role's data is now null.
// Before Save
UserRoleRelationship [id=null, person=Person [id=26, name=TestUser, roles=1], **role=Role [id=27, roleType=ADMIN, defaultPrivileges=[PRIV4, PRIV1, PRIV2, PRIV3]]**]
// After Save
UserRoleRelationship [id=6, person=Person [id=26, name=TestUser, roles=0], **role=Role [id=27, roleType=null, defaultPrivileges=null]**]
Any ideas as to why this is occurring to my Role Object?
Also, here are my Repos
public interface PersonRepository extends CrudRepository<Person, String> {
Person findByName(String name);
}
public interface RoleRepository extends CRUDRepository<Role> {
Role findByRoleType(RoleType rt);
Role findByRoleName(String name);
}
public interface UserRoleRepository extends CrudRepository<UserRoleRelationship, String> {}
It repopulates the UserRoleRelationship after saving.
As you don't have a #Fetch annotation on neither the start nor end-node it only loads them in a shallow fashion.
I don't recommend to add #Fetch there because it can easily load your whole graph, so I'd probably use
template.fetch(rel.person);
template.fetch(rel.role);
I am trying to delete all Type's from my Neo4j database. I have a repository for the Type class, typeRepository, which I call typeRepository.deleteAll();. However, not everything is deleted. Only its node is deleted leaving the BusinessLogic node alive in the database. I am not sure what else to try at this point, since it name of the method implies that it will delete all things including itself and things related to itself. Here is how my persisted class looks, which extends a base type of object that my database contains:
#NodeEntity
public class BaseType {
#GraphId
private Long id;
#Indexed(unique=true) String uid;
private String name;
BaseType() {}
BaseType(String name) {
this.name = name;
}
}
,
public class Type extends BaseType {
#RelatedTo(type="businessLogic")
#Fetch
private BusinessLogic businessLogic;
public Type() {super();}
public Type(String name, BusinessLogic businessLogic) {
super(name);
this.businessLogic = businessLogic;
}
}
,
#NodeEntity
#XmlAccessorType(XmlAccessType.NONE)
public class BusinessLogic implements Serializable {
#GraphId
private Long id;
private static final long serialVersionUID = -634875134095817304L;
#XmlElement
private String create;
public void setCreate(String create) {
this.create = create;
}
public String getCreate() {
return create;
}
}
I only store the Type instances, and I do that by calling
typeRepository.save(new Type(name, businessLogic));.
I don't think SDN is doing cascading delete on its own. So, why don't you first delete the BusinessLogic objects via their respective repository, and then the Type objects?
I am implementing a banking application and have three tables in my database (User, Account and AccountActivity):
The implementation of the Account and AccountActivity classes look like this:
#MappedSuperclass public abstract class AbstractDomain implements Serializable {
#Id #GeneratedValue
private long id = NEW_ID;
public static long NEW_ID = -1;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isNew() {
return id==NEW_ID;
}
}
#Table(name="ACCOUNT_ACTIVITY")
#Entity
public class AccountActivity extends AbstractDomain {
#Column(name="NAME")
private String Name;
#Column(name="XDATE")
private Date Date;
#Column(name="VALUE")
private double Value;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="ACCOUNTID")
private Account ACCOUNT;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="OTHERACCOUNTID")
private Account OTHERACCOUNT;
public String getName() {
return Name;
}
// ...
}
And:
#Table(name="ACCOUNT")
#Entity
public class Account extends AbstractDomain {
#Column(name="NAME")
private String Name;
#Column(name="XDATE")
private Date Date;
#Column(name="VALUE")
private double Value;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="USERID")
private User USER;
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private List<AccountActivity> AccountActivity = new ArrayList<AccountActivity>();
// ...
}
To store new accounts in my database I use this:
public Account storeAccount(Account ac) {
User x = ac.getUser();
x = em.merge(x);
ac = em.merge(ac);
return ac;
}
which works to just store new accounts in my database. I wanted to implement the functionality that when account activity information is added to an already saved account,
that account will be updated and the added information (account activity) is cascaded to the
AccountActivity table using this piece of code:
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
private List<AccountActivity> AccountActivity = new ArrayList<AccountActivity>();
When I test this code I get the error:
java.sql.SQLException: Integrity constraint violation -no parent
FK670B7D019607336A table: ACCOUNT_ACTIVITY in statement
Can anybody help me with this problem?
update
I test with this piece of junit code:
public void testAddAccountActivities() {
User user = dummyPersistedUser();
User user2 = dummyPersistedUser();
Account account = getTestUtils().dummyEmptyAccount(user);
Account account2 = getTestUtils().dummyEmptyAccount(user2);
account=accountManager.storeAccount(account);
account2=accountManager.storeAccount(account2);
getTestUtils().fillAccounts(account, account2);
accountManager.storeAccount(account);
accountManager.storeAccount(account2);
assertEquals(2,accountManager.getAccount4Name(account.getName()).getAccountActivity().size());
assertEquals(2,accountManager.getAccount4Name(account2.getName()).getAccountActivity().size());
}
where fillAccounts(account, account2) just inserts some AccountActivities that should be added to the graph.:
AccountActivity aa = new AccountActivity();
aa.setDate(new Date());
aa.setName("test activity");
aa.setAccount(a1);
aa.setValue(value);
aa.setOtherAccount(a2);
account.addAccountActivity(aa)
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinColumn(name ="accountid") // you have several references from AccountActivity to Account. You need to specify the join column in this case.
private List<AccountActivity> AccountActivity = new ArrayList<AccountActivity>();
As a first observation, I think you shouldn't have the variable name the same as the class. So, instead of this private List <AccountActivity> AccountActivity, you can write something like private List <AccountActivity> accountActivity.