I have a Hibernate model with id, name and surname. I am using it to get data from the database and then at the GET end-point is this one:
#GetMapping(value = "/contacts", produces = MediaType.APPLICATION_JSON_VALUE)
public List<Contact> allContacts() {
return contactService.findAll();
}
As you can see it returns the Contact object. Actually it is a Hibernate entity.
The problem is that when I use this code
#PostMapping("/contacts")
public Contact createContact(Contact contact) {
return contactService.createContact(contact);
}
it asks not only name and surname but also the id. POST methods should not ask for id since they are not created yet. What should I do so that it doesn't ask for an id?
Edit: Here is the Contact.java class
import lombok.Data;
import javax.persistence.*;
#Entity
#Data
public class Contact {
public Contact() {
}
public Contact(Integer id, String name, String surname) {
this.id = id;
this.name = name;
this.surname = surname;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(columnDefinition = "serial")
private Integer id;
private String name;
private String surname;
}
Define a ContactInput class that only has the attributes you want the user to input and then create some mapping code that creates a valid Contact based on the ContactInput.
You should create ContactDto class
#Data
public ContactDto class {
private String name;
private String surname;
}
In #PostMapping you are gonna get ContactDto from the user. You cannot saved ContactDto into your database. So you need to map ContactDto to Contact. What you can do is simply create ContractMapper class.
public static contactDtoToEntity(ContactDto dto){
Contact dbContact = new Contact();
dbContact.setName(dto.getName());
dbContact.setSurname(dto.getSurname());
return dbContact;
}
Before you saved the contact in your database in service layer, you need to map it and then save. Id is gonna be generated in the database.
Related
I'm developing a Banking system Project Where User Should be able to create an account in any available branch. Branch Creation will be maintained by admin. I had two tables(Entities) like BranchDetails and AccountDetails.
The BranchId Column is the primary key in BranchDetails and Foreign key in AccountDetails. Now When user Creates an account, he will input the Preferred Branch name and the AccountHolder name. I had to Insert this Account in AccountDetails Table which matching the branchId which the user had entered.
How do i achive this. So far i have tried,
BranchDetails.java
#Entity
#Table( name = "branchdetails")
public class BranchDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int branchId;
#Column( name = "branchName")
private String branchName;
#OneToOne(mappedBy = "branchdetails")
private AccountDetails accountDetails;
/getters and setters
}
AccountDetails.java
#Entity
#Table(name = "accountdetails")
public class AccountDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int customerId;
#Column(name = "customerName")
private String customerName;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "branchId")
private BranchDetails branchdetails;
/getters and setters
}
Controller
#Controller
public class ApiController{
#RequestMapping(value = "/branchcreation", method = RequestMethod.POST
,consumes = {"application/x-www-form-urlencoded"})
#ResponseBody
public String brCreation( BranchDetails br, String branchname){
br.setBranchName(branchname);
branchrepository.save(br);
return "Sucesspage";
}
#RequestMapping(value = "/accountcreation", method = RequestMethod.POST)
#ResponseBody
public String AcCreation(AccountDetails ad,BranchDetails br, String branchname,String customername){
br.setBranchName(branchname);
ad.setCustomerName(customername);
accountrepository.save(ad);
return "Sucesspage";
}
}
Prepare AcCreation method to receive branch id (the branches already exist, so you could send their ids and names to your frontend form's select component) and customer name (provided by the user in the input field):
It would look like this (I changed the name to createAccount, because it sounds naturally, there is no need to use shortcuts in method's name, but in the end it's your choice):
#RequestMapping(value = "/account-creation", method = RequestMethod.POST)
#ResponseBody
public String createAccount(String customerName, Integer branchId){
accountRepository.createAccount(customerName, branchId);
return "Sucesspage";
}
Look at the removed code from the service method.
Details connected with creation on the database should be contained by database access layer, in this case the class AccountRepository and database layer methods should be called by service's methods - in your case we left out the service class).
So you would create Account instance inside its method and then set the branchId field.
Or you could do something like this (you would have to have two separate repositories, one for AccountDetails, second for BranchDetails entities):
#RequestMapping(value = "/account-creation", method = RequestMethod.POST)
#ResponseBody
public String createAccount(String customerName, Integer branchId){
BranchDetails branchDetails = branchRepository.find(branchId);
AccountDetails accountDetails = new AccountDetails();
accountDetails.setCustomerName(customerName);
accountDetails.setBranchDetails(branchDetails);
accountRepository.save(accountDetails);
return "Sucesspage";
}
I have developed two tables in Spring Boot, User and UserMeta. User is the parent and UserMeta is the child table. The foreign-key is user_id. I may be looking at it the wrong way, but I want to be able to first create an entity of User. Then, I want to create an entity of UserMeta. Simply UserMeta should contain additional data to User.
However, when first creating a User and then a UserMeta entity, I get e new User entity (ending up with two User entities and one UserMeta entity.)
The problem I think is that I create a UserMeta object with a User, since I want to have a relationship between User and UserMeta. But if I want to be able to first create a User and then a UserMeta, should I simply ignore a foreign-key? Or, does it exists another way of creating a UserMeta entity without creating a new User?
User
public class User {
#Id
#SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
//#OneToOne(optional=false)
private Long userId;
private String username;
private String password;
private String email;
#OneToOne(mappedBy = "user")
private UserMeta userMeta;
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
}
UserMeta
public class UserMeta {
#Id
#SequenceGenerator(name = "user_meta_sequence", sequenceName = "user_meta_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_meta_sequence")
private Long userMeta_Id;
private String lastname;
private int age;
#OneToOne(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
optional = false
)
#JoinColumn(
name = "user_Id",
referencedColumnName="userId"
)
private User user;
public UserMeta(String lastName, int age, User user){
this.lastname = lastName;
this.age = age;
this.user = user;
}
}
UserRepository
public interface UserRepository extends CrudRepository<User, Long> {
}
UserService
public interface UserService {
User saveUser(User user);
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
super();
this.userRepository = userRepository;
}
#Override
public User saveUser(User user) {
// TODO Auto-generated method stub
return this.userRepository.save(user);
}
UserController
#RestController
public class UserController {
private UserService userService;
public UserController(UserService userService) {
super();
this.userService = userService;
}
#PostMapping("/user")
public ResponseEntity<User> saveUser(#RequestBody User user) {
return new ResponseEntity<User>(userService.saveUser(user), HttpStatus.CREATED);
}
}
UserMetaRepository
public interface UserMetaRepository extends CrudRepository<UserMeta, Long> {
}
UserMetaService
public interface UserMetaService {
UserMeta saveUserMeta(UserMeta userMeta);
}
UserMetaServiceImpl
#Service
public class UserMetaServiceImpl implements UserMetaService{
private UserMetaRepository userMetaRepo;
public UserMetaServiceImpl(UserMetaRepository userMetaRepo) {
super();
this.userMetaRepo = userMetaRepo;
}
#Override
public UserMeta saveUserMeta(UserMeta userMeta) {
return this.userMetaRepo.save(userMeta);
}
}
UserMetaController
#RestController
public class UserMetaController {
public UserMetaService userMetaService;
public UserMetaController(UserMetaService service) {
super();
this.userMetaService = service;
}
#PostMapping("/userMeta")
public ResponseEntity<UserMeta> saveUserMeta(#RequestBody UserMeta userMeta) {
return new ResponseEntity<UserMeta>(this.userMetaService.saveUserMeta(userMeta), HttpStatus.CREATED);
}
}
you should use this constructor in the User class,
public User(String username, String email, String password, UserMeta userMeta) {
this.username = username;
this.email = email;
this.password = password;
this.userMeta = userMeta;
}
now when you save your user the user Meta will be added to your UserMeta table,
If you want to add a user Meta to an existing user you will only need to set the userMeta and save it with a simple userRepository.save(theUpdatedUser)
you can also create userMeta seperately with your code above, and if you want to assign it to a user already in data base or not you can allows use the power of spring data and use simple userRepository.save(userWithTheAssignedMeta)
the same logic applies the other way for metaUser.
The problem here is that your UserMetadata creation logic is using incomplete JSON:
{ "lastName":"foo", "age":1, "user":{ "username":"foo", "password":"bar", "email":"foo-bar" } }
Within this, the problem is the 'user' has all the data, duplicating what was already created the the database, but does not identify it. Since the mapping has cascade.ALL set on it, Spring/JPA will persist the UserMetadata and find this User instance that doesn't have identity, so persist it - giving it identity from the sequence.
There are a few ways you might correct this. First and easiest is to send the User ID in the json from the previously created instance:
{ "lastName":"foo", "age":1, "user":{ "userId":1, "username":"foo", "password":"bar", "email":"foo-bar" } }
This will allow Spring/JPA to recognize the user's identity and merge it and the data provided into the database. It means though that you must send complete data for the User - it will push incomplete data into the DB.
If that is a concern, you can change the cascade options. You may not want cascading persist/merge at all on this relationship, and I suspect when you delete userMetadata you don't really want to delete the User instance, so I think this might have been done incorrectly (maybe put it on the user->UserMetadata relationship instead?). If you remove the cascade settings, spring/JPA will let you just pass in JSON with the USER id specified, as this gives it enough to set the fk:
{ "lastName":"foo", "age":1, "user":{ "userId":1} }
Let's say I have two entities:
#Entity
public class Phone {
#Id
private Long id;
private String number;
}
#Entity
public class Person {
#Id
private Long id;
private String name;
}
The relationship between a person and a phone is one to one.
How could I access only the phone's number in the Person entity mapped by the phone's id
#Entity
public class Person {
#Id
private Long id;
private String name;
// ???
private String phoneNumber;
}
The reason for not mapping the whole entity is because in some more realistic entities there are too many properties.
I don't think you can, but something like this might be acceptable:
public class Person {
#OneToOne
#JoinColumn(name = "phone_id")
private Phone phone;
public String getPhoneNumber() {
return phone.getNumber();
}
}
Although you have mapped the whole object, not just the single property, you have only exposed the single property you want. The other stuff is hidden.
Alternatively, do it at the DB layer using a View:
create view person_with_phone as
select p.id, p.name,f.number
from person p
join phone f on f.id=p.phone_id
and then have an entity class to match the view. You'll need to turn off schema creation in your JPA implementation.
I have defined customer entity
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and CrudRepository
public interface CustomerRepo extends CrudRepository<Customer, Long> {
}
if I use CustomerRepo.findById method for finding Customer
#Autowired
CustomerRepo repo;
Optional<Customer> dbCustomer = repo.findById(id);
how can I get name of that customer. I cannot use getter then.
so I'm interested is there any solution of using getters of Optional, or I need to use other method for finding Customer by id?
Optional<Customer> is returned, because it is not guaranteed that there will be such a customer with the requested ID in the database.
Instead of returning null it simply means that Optional.isPresent() will return false when the ID does not exist.
According to the API Docs (https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html#findById-ID-):
Returns:
the entity with the given id or Optional#empty() if none found
You will therefore probably want to simply use the methods on Optional to check whether it contains a Customer (i.e. a Customer with that ID exists), and then get the name like so:
Optional<Customer> dbCustomer = repo.findById(id);
if(dbCustomer.isPresent()) {
Customer existingCustomer = dbCustomer.get();
String nameWeWanted = existingCustomer.getName();
//operate on existingCustomer
} else {
//there is no Customer in the repo with 'id'
}
Alternatively you can try callback style (shown with Java 8 Lambda):
Optional<Customer> dbCustomer = repo.findById(id);
dbCustomer.ifPresent(existingCustomer -> {
String nameWeWanted = existingCustomer.getName();
//operate on existingCustomer
});
It is worth noting that it is possible to check existence of the ID without actually retrieving/loading the entity by using the interface method:
boolean CrudRepository.existsById(ID id)
This saves an entity load, but it still requires a database roundtrip.
Try to use another method for finding Customer:
#Autowired
CustomerRepo repo;
Customer dbCustomer = repo.findOne(id);
Spring LdapRepository save() method throws exception when I'm trying to update an existing object in LDAP database.
org.apache.directory.api.ldap.model.exception.LdapEntryAlreadyExistsException: ERR_250_ENTRY_ALREADY_EXISTS
What method should I use to update existing ldap objects?
Person class:
#Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" })
public class Person implements Serializable {
public Person() {
}
#Id
private Name dn;
#Attribute(name = "cn")
#DnAttribute(value = "cn")
#JsonProperty("cn")
private String fullName;
#Attribute(name = "uid")
private String uid;
private String mail;
#Attribute(name = "sn")
private String surname;
//setters and getters
}
Person repo interface:
public interface PersonRepo extends LdapRepository<Person> {
}
That's how I'm updating person:
personRepo.save(person);
Default implementation for Spring LDAP repositories is SimpleLdapRepository, that checks the property annotated with #Id to determine if the objects is new - and perform create, or old - and perform update.
I'm guessing that Person.dn is null when you're trying to perform update.
You also can take the control over this by implementing org.springframework.data.domain.Persistable and place your logic in the isNew() method.
See the implementation details.