#OneToMany Pesist Child when creating Parent Entity - postgresql

I have a OneToMany Relationship (User to EmailAddress)
Maybe I'm going about this the wrong way, My Database is empty but If I want to POST a User object and add it to the Database, along with the emailAdresses object and have the EmailAddress persisted also.
I want 2 records in the Database:
1 User and 1 EmailAddress (with a fk to User table)
Service Class
Currently what I've implemented to get this to work is this:
#Service
public class UserService {
private UserRepository userRepository;
private ModelMapper modelMapper;
public UserService(UserRepository userRepository, ModelMapper modelMapper) {
this.userRepository = userRepository;
this.modelMapper = modelMapper;
//Used for mapping List
modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(Configuration.AccessLevel.PRIVATE)
.setSourceNamingConvention(NamingConventions.JAVABEANS_MUTATOR);
}
public User createUser(UserCreateDTO userCreateDTO) {
User user = modelMapper.map(userCreateDTO, User.class);
//persist User to EmailAddress object
if(user.getEmailAddresses() != null){
user.getEmailAddresses().forEach(user::persistUser);
}
return userRepository.save(user);
}
public UserDTO getUserById(Long id) {
User found = userRepository.findById(id).get();
return modelMapper.map(found, UserDTO.class);
}
// .....
Which I have seen used in some bidirectional relationships
User Entity
#Entity
#Table(name = "Users")
#Getter #Setter #ToString #NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", updatable = false, nullable = false)
private Long id;
private String firstName;
private String lastName;
private int age;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<EmailAddress> emailAddresses;
Email Address Entity
#Entity
#Table(name = "Email")
#Getter #Setter #ToString #NoArgsConstructor
public class EmailAddress {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="email_id", updatable = false, nullable = false)
private Long emailId;
private String email;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST )
#JoinColumn(name = "user_id", nullable = false)
#JsonIgnore
private User user;
Is there a better way to set up the Join relationship?
Sample POST Request
{"firstName":"Joe", "lastName":"Bloggs", "age": 30, "emailAddresses" : [ "joe-private#email.com" , "joe-work#email.com" ] }

I guess you need to associate this email with a user as well, not just set user to email entity.
public void persistUser(EmailAddress emailAddress) {
// set this email to the user
// if email EmailAddresses list is null you might need to init it first
this.getEmailAddresses().add(emailAddress);
emailAddress.setUser(this);
}

Firstly, I believe that a method persistUser should not be a part of a service layer - due to its implementation it mostly like a Domain layer method that should be implemented within a User entity class.
Secondly, since it's a POST method you shouldn't care of emails existence - you are adding a new user with a new set of emails
Closer to a question, I'd suggest you to try this:
public class UserService {
/************/
#Autowired
private UserManager userManager;
public void addUser(UserModel model) {
User user = new User(model);
List<EmailAddress> emails = model.getEmailAddresses().stream().map(EmailAddress::new).collect(Collectors.toList());
user.setEmailAddresses(emails);
userManager.saveUser(user);
}
}
and at the User add this:
public void setEmailAddresses(List<EmailAddress> emails) {
emails.forEach(item -> item.setUser(this));
this.emailAddresses = emails;
}
And don't forget to implement constructors for entities with model paremeters

Related

Jpa not inserting the record in the join table for many to many relatioship

I have a Many-to-Many relationship with User and Role JPA entities. When I try to save the entities, both User and Role entities gets persisted in the table, but the junction table is not getting inserted with the records, Where am I going wrong
User Entity
#Entity
#Table(name="users")
#Data
#NoArgsConstructor
#EqualsAndHashCode(exclude = "roles")
#ToString(exclude = "roles")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String password;
private double salary;
public User(String name, String password, double salary) {
super();
this.name = name;
this.password = password;
this.salary = salary;
}
#ManyToMany(
mappedBy = "users")
private Set<Role> roles = new HashSet<>();
public void addRole(Role role) {
this.roles.add(role);
role.getUsers().add(this);
}
}
Role Entity
#Entity
#Table(name = "roles")
#Data
#NoArgsConstructor
#EqualsAndHashCode(exclude = "users")
#ToString(exclude = "users")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String roleName;
public Role(String roleName) {
super();
this.roleName = roleName;
}
#ManyToMany
#JoinTable(
name="user_roles",
joinColumns = #JoinColumn(name="role_id", nullable = false),
inverseJoinColumns = #JoinColumn(name="user_id", nullable = false)
)
private Set<User> users = new HashSet<>();
}
Client class
#EventListener(ApplicationReadyEvent.class)
public void onApplicationStartup(ApplicationReadyEvent event) {
User kiran = new User("kiran", this.passwordEncoder.encode("welcome"), 4500000);
User vinay = new User("vinay", this.passwordEncoder.encode("welcome"), 4500000);
Role userRole = new Role("ROLE_USER");
Role adminRole = new Role("ROLE_ADMIN");
kiran.addRole(userRole);
vinay.addRole(userRole);
vinay.addRole(adminRole);
this.userRepository.save(kiran);
this.userRepository.save(vinay);
}
Where am I going wrong?
You've mapped a bidirectional relationship, but are only setting one side of it in your object model - the wrong side. Should there ever be a discrepancy, the owning side controls the values of foreign keys, and since you have left the owning side empty, they aren't being set. You are responsible to set both sides of relationships and keeping them in synch with what you want in the database.
Since you don't have cascade options set on the relationships, you are also responsible for persisting the roles independently from the Users. Something more like:
public void onApplicationStartup(ApplicationReadyEvent event) {
// you might want to check to see if these roles already exists and use those instead of creating new ones
Role userRole = roleRepository.save(new Role("ROLE_USER"));
Role adminRole = roleRepository.save(new Role("ROLE_ADMIN"));
User kiran = new User("kiran", this.passwordEncoder.encode("welcome"), 4500000);
kiran.addRole(userRole);//assumes this adds the user to the role.users as well.
this.userRepository.save(kiran);
User vinay = new User("vinay", this.passwordEncoder.encode("welcome"), 4500000);
vinay.addRole(userRole);
vinay.addRole(adminRole);
this.userRepository.save(vinay);
}
Also, you are using Set in your entities with Lombok using "#EqualsAndHashCode" generation. Don't do that!
Set uses the equals/hashcode logic to determine if two objects are the same to filter out duplicates, while Lombok generates those methods to use what are mutable fields. In the case you have new entities in those sets (ie this usecase), the IDs are null and will change when set from JPA. You are better off keeping Java equals/hashcode logic if you don't know what effects those will have on your application. try using either List in your model and/or not having Lombok generate those method for you.

Do I need to set a childs parent?

I am having an issue where the Domain (child) is not getting the Business (parents) id. I have the business created and the domain but the domain does not have the business_id. Below is what I was doing originally. I then made a test path that I called which gave the same behavior unless I used the setBusiness method of the domain. Is this the expectation? Do I need to loop over the domains that are passed from a client and set the business?
Test
public void test() {
Business b = new Business();
b.setName("Test Test");
Domain d1 = new Domain();
d1.setName("Domain 1");
d1.setBusiness(b);
em.persist(b);
em.flush();
}
Code
#Entity
#Table(name = "Business")
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy="business", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Domain> domains = new ArrayList<>();
//...getters and setters
}
-
#Entity
#Table(name = "Domain")
public class Domain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "business_id", nullable = false)
private Business business;
//...getters and setters
}
I was calling a rest service using the below JSON which then persisted the business.
{
"name": "Business Test ",
"domains": [{"name": "test domain"}]
}
#Path("business")
public class BusinesResouce {
#EJB
BusinessService service;
#Path("create")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void create(Business entity) {
service.persist(entity);
}
//other paths
}
-
#Stateless
public class BusinessService {
private static final String PERSISTENCE_UNIT_NAME = "edc";
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
private EntityManager em;
public void persist(Business entity) {
em.persist(entity);
//Do I really need to loop entity.getDomains and setBusiness?
em.flush();
}
}

JPA OneToOne not working

I followed by tutorial : http://www.codejava.net/frameworks/hibernate/hibernate-one-to-one-mapping-with-foreign-key-annotations-example
I have following code:
#Entity
#Table(name = DomainConstant.TABLE_USER)
public class User{
#Id
#Column(name = DomainConstant.DOMAIN_USER_ID)
#GeneratedValue
private Long userId;
private UserActivationCode userActivationCode;
///////////////////// CONSTRUCTOR....
/// STANDARD GET AND SET....
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = DomainConstant.DOMAIN_ACTIVATION_LINK_ID)
public UserActivationCode getUserActivationCode() {
return userActivationCode;
}
}
#Entity
#Table(name = DomainConstant.TABLE_USER_ACTIVATON_LINK)
public class UserActivationCode {
#Id
#Column(name = DomainConstant.DOMAIN_ACTIVATION_LINK_ID)
#GeneratedValue
private Long userActivationCodeId;
#Column(name = DomainConstant.DOMAIN_ACTIVATION_DATE)
#Temporal(javax.persistence.TemporalType.DATE)
private Date date;
#Column(name = DomainConstant.DOMAIN_ACTIVATION_CODE)
private String code;
///////////////////// CONSTRUCTOR....
/// STANDARD GET AND SET....
}
When I save the User object it does not make record in UserActivationCode, why?
Like this:
User newUser = new User();
newUser.setUserActivationCode(new UserActivationCode("this is example"));
userDao.save(newUser);
I have record only in user table.
Can you tell me why?
Your problem is that you are mixing access types. In the User entity you have specified #Id on a field (private Long userId) whereas you have defined the join mapping on a property (the getter to UserActivationCode). If you specify the join mapping on the field, it should work as is.
#Entity
#Table(name = DomainConstant.TABLE_USER)
public class User{
#Id
#Column(name = DomainConstant.DOMAIN_USER_ID)
#GeneratedValue
private Long userId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = DomainConstant.DOMAIN_ACTIVATION_LINK_ID)
private UserActivationCode userActivationCode;
///////////////////// CONSTRUCTOR....
/// STANDARD GET AND SET....
public UserActivationCode getUserActivationCode() {
return userActivationCode;
}
}
For more information on access and access types, see Access, Java EE 7

Spring Data with Integration Test OneToMany ManyToOne

I know that this topic is not new, but I few days try to solve task.
I have 3 classes - Client, Account and Invoice.
Client has many Accoutns, Account has many Invoices.
I mapped them:
Client
#Entity
#Table(name = "CLIENT")
public final class Client implements Serializable {
...
#Column(length = 36, nullable = false, unique = true, updatable = false)
private String uuid;
#OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
private List<Account> accounts;
...
}
Account
#Entity
#Table(name = "ACCOUNT")
public final class Account implements Serializable {
...
#ManyToOne
#JoinColumn(name = "client_uuid", referencedColumnName = "uuid", nullable = false)
private Client client;
#OneToMany(mappedBy = "account", cascade = CascadeType.ALL)
private List<Invoice> invoices;
...
}
Invoice
#Entity
#Table(name = "INVOICE")
public final class Invoice implements Serializable {
#ManyToOne
#JoinColumn(name = "account_uuid", referencedColumnName = "uuid", nullable = false)
private Account account;
}
I use Spring Data Jpa:
#Repository
public interface SpringDataClientRepository extends ClientRepository, JpaRepository<Client, Integer> {
Others the same.
When I try to run ITests, test with client work fine:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { JpaConfig.class, ITDaoConfig.class })
public class ITClientRepository {
private static final Logger log = Logger.getLogger(ITClientRepository.class);
#Autowired
private ClientRepository clientRepository;
#Test
#Transactional
public void testSaveClient() {
log.info("testSaveClient start");
Client client = new Client();
client.setName("testSaveClient");
client.setUuid("client-testSaveClient");
client.setTelephone("12345679");
Account account = new Account();
account.setClient(client);
account.setMoneyCount(10);
account.setUuid("client-testSaveClient");
client.addAccount(account);
log.info(client.toString());
Client getClient = clientRepository.save(client);
log.info(client.toString());
It saves client and account in. But this test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { JpaConfig.class, ITDaoConfig.class })
public class ITAccountRepository {
private static final Logger log = Logger.getLogger(ITAccountRepository.class);
#Autowired
private AccountRepository accountRepository;
#Test
#Transactional
public void testSaveAccount() {
log.info("testSaveAccount start");
Client client = new Client();
client.setName("testSaveAccount");
client.setTelephone("12345679");
client.setUuid("account-testSaveAccount");
client.setId(200);
// Client saved in db
Account account = new Account();
account.setClient(client);
account.setMoneyCount(15);
account.setUuid("account-testSaveAccount");
client.addAccount(account);
Invoice invoice = new Invoice();
invoice.setAccount(account);
invoice.setAmount(11);
Date date = new Date();
invoice.setCreated(date);
invoice.setUuid("account-testSaveClient");
invoice.setDescription("Description of invoice");
account.addInvoice(invoice);
log.info(account.toString());
Account getAccount = accountRepository.save(account);
log.info(account.toString());
fail with:
Caused by: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : projects.model.Invoice.account -> projects.model.Account
I want that all Invoices will be saved if I save Account of those invoices. And the same with Client - all Accounts will be saved if I save Client of them. How I do this?
I made a change to have a unidirectional relationship with composition:
Client
#Entity
#Table(name = "CLIENT")
public final class Client extends BaseEntity {
#Column(length = 36)
private String uuid;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "client_id", referencedColumnName = "id")
private List<Account> accounts;
}
Invoice
#Entity
#Table(name = "INVOICE")
public final class Invoice extends BaseEntity {
#Column(length = 36)
private String uuid;
}
Account
#Entity
#Table(name = "ACCOUNT")
public final class Account extends BaseEntity {
#Column(length = 36)
private String uuid;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinColumn(name = "account_id", referencedColumnName = "id")
private List<Invoice> invoices;
}
In the test, I left everything like it was. And it all works now.

How to correctly do a manytomany join table in JPA?

I need 3 entities: User, Contract (which are a many to many relation) and a middle entity: UserContract (this is needed to store some fields).
What I want to know is the correct way to define the relationships between these entities in JPA/EJB 3.0 so that the operations (persist, delete, etc) are OK.
For example, I want to create a User and its contracts and persist them in a easy way.
Currently what I have is this:
In User.java:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<UserContract> userContract;
In Contract.java:
#OneToMany(mappedBy = "contract", fetch = FetchType.LAZY)
private Collection<UserContract> userContract;
And my UserContract.java:
#Entity
public class UserContract {
#EmbeddedId
private UserContractPK userContractPK;
#ManyToOne(optional = false)
private User user;
#ManyToOne(optional = false)
private Contract contract;
And my UserContractPK:
#Embeddable
public class UserContractPK implements Serializable {
#Column(nullable = false)
private long idContract;
#Column(nullable = false)
private String email;
Is this the best way to achieve my goals?
Everything looks right. My advice is to use #MappedSuperclass on top of #EmbeddedId:
#MappedSuperclass
public abstract class ModelBaseRelationship implements Serializable {
#Embeddable
public static class Id implements Serializable {
public Long entityId1;
public Long entityId2;
#Column(name = "ENTITY1_ID")
public Long getEntityId1() {
return entityId1;
}
#Column(name = "ENTITY2_ID")
public Long getEntityId2() {
return entityId2;
}
public Id() {
}
public Id(Long entityId1, Long entityId2) {
this.entityId1 = entityId1;
this.entityId2 = entityId2;
}
}
protected Id id = new Id();
#EmbeddedId
public Id getId() {
return id;
}
protected void setId(Id theId) {
id = theId;
}
}
I omitted obvious constructors/setters for readability. Then you can define UserContract as
#Entity
#AttributeOverrides( {
#AttributeOverride(name = "entityId1", column = #Column(name = "user_id")),
#AttributeOverride(name = "entityId2", column = #Column(name = "contract_id"))
})
public class UserContract extends ModelBaseRelationship {
That way you can share primary key implementation for other many-to-many join entities like UserContract.