Spring-data-mongodb not persist multiple objects on list - java

I am using Spring-data-mongodb and i can persist an object on a list, but when i try to add another, it doesn't work, the application doesn't throw an exception.
this is my Json:
[
{
idUser: "4a9f10d9-e19f-42af-ba00-891a567cc41f",
login: "peter",
password: "mypassword",
email: "peter#eeee.com",
patients:
[
{
idPatient: "d31e8052-36d3-4285-9f97-454f3437812d",
name: "ada",
birthday: 1363474800000,
idUser: "4a9f10d9-e19f-42af-ba00-891a567cc41f",
region:
{
idRegion: "d8acfa45-486e-49e0-b4e6-edde6743cf30",
name: "Madrid"
},
personalCalendars: null
},
null
]
}
]
As you can see, my first Patient element is correctly, and the second was insert as null.
I leave my code:
User.java
#Document(collection = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
#Indexed
private UUID idUser;
#Indexed(unique = true)
private String login;
private String password;
#Indexed(unique = true)
private String email;
#DBRef
private List<Patient> patients;
#PersistenceConstructor
public User(UUID idUser, String login, String password, String email, List<Patient> patients){
this.idUser = idUser;
this.login = login;
this.password = password;
this.email = email;
this.patients = patients;
}
Patient.java
#Document(collection = "patients")
public class Patient implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String id;
#Indexed
private UUID idPatient;
private String name;
private Date birthday;
private UUID idUser;
private Region region;
#Transient
private List<PersonalCalendar> personalCalendars;
#PersistenceConstructor
public Patient(UUID idPatient, String name, Date birthday,UUID idUser, Region region){
this.idPatient = idPatient;
this.name = name;
this.birthday = birthday;
this.idUser = idUser;
this.region = region;
}
and the DAO whereI do the insert.
#Override
public Patient createPatient(User user, Patient patient) {
this.mongoOps.save(patient , "patients");
this.mongoOps.save(user , "users");
return this.getPatientById(patient.getIdPatient());
}
The console returns this, but no persists the patient:
15:16:16.718 [tomcat-http--6] DEBUG o.s.data.mongodb.core.MongoTemplate - Saving DBObject containing fields: [_class, _id, idPatient, name, birthday, idUser, region]
15:16:16.723 [tomcat-http--6] DEBUG o.s.data.mongodb.core.MongoDbUtils - Getting Mongo Database name=[application]
15:16:16.747 [tomcat-http--6] DEBUG org.mongodb.driver.protocol.insert - Inserting 1 documents into namespace application.patients on connection [connectionId{localValue:2, serverValue:119}] to server 127.0.0.1:27017
15:16:16.761 [tomcat-http--6] DEBUG org.mongodb.driver.protocol.insert - Insert completed
I need help.
Thanks a lot

First, if you use Spring Data with MongoDB, use it properly:
#Repository
public interface UserRepository extends MongoRepository<User, String> {
}
Now just inject UserRepository via #Autowired annotation:
#Autowired
private UserRepository userRepository;
User user = new User();
Patient patient = new Patient();
user.addPatient(patient);
// Just call save from userRepository to save your User with Patient.
// save method will return instance of saved user (together with instance of
// patient)
User user = userRepository.save(user);
Note that save method can also be used for updating of existing User. If User is new (not having generated id) it will be inserted. If user exists (has generated id) it will be just updated.
Presuming that User class has a addPatient method that looks like this:
public void addPatient(Patient patient) {
this.patients.add(patient);
}
Also, make sure that your list is initialized: List<Patient> patients = new ArrayList<>();

Related

Update User Detail api returns DataIntegrityViolationException

I'm creating an update API that updates the profile of the super admin, I mapped the member table to a DTO, on the member table password is set to not null and I did not include the password field on the dto because there's a provision for that be, when I tested the API on postman it returned on the console
DataIntegrityViolationException
SQL Error: 1048, SQLState: 23000
Column 'password' cannot be null
Here is my code
Dto
#Getter
#Setter
public class UpdateProfileDto {
#NotNull(message = "{member.firstName.notNull}")
#JsonProperty("first_name")
private String firstName;
#NotNull(message = "{member.lastName.notNull}")
#JsonProperty("last_name")
private String lastName;
#JsonProperty("nationality")
private Long nationality;
#JsonProperty("country_of_residence")
private Long countryOfResidence;
#JsonProperty("date_of_birth")
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
#JsonFormat(pattern = "dd-MM-yyyy")
#Past(message = "{customer.dateOfBirth.past}")
private Date dateOfBirth;
#JsonProperty("current_job_title")
private String currentJobTitle;
#NotNull(message = "{member.emailAddress.notNull}")
#JsonProperty("email_address")
private String emailAddress;
#JsonProperty("username")
private String username;
#NotNull(message = "{member.phoneNumber.notNull}")
#PhoneNumber
#JsonProperty("phone_number")
private String phoneNumber;
#Size(max = 300, message = "{member.city.size}")
#JsonProperty("city")
private String city;
#Size(max = 300, message = "{member.state.size}")
#JsonProperty("state")
private String state;
}
ServiceImpl
#Override
#Transactional
public Member updateProfile(UpdateProfileDto body) {
Member superAdmin = repository.getOne(id);
if (superAdmin == null) {
throw new MemberNotFoundException(id);
}
Optional<Role> existingRole = roleJpaRepository.findByCode(RoleType.SUPER_ADMINISTRATOR.getValue());
if (existingRole.isEmpty()) {
throw new RoleNotFoundException(RoleType.SUPER_ADMINISTRATOR.getValue());
}
Member existing;
existing = mapper.map(body, Member.class);
existing.setPassword(superAdmin.getPassword());
existing.getRoles().add(existingRole.get());
existing.setNationality(countryRepository.getOne(body.getNationality()));
existing.setCountryOfResidence(countryRepository.getOne(body.getCountryOfResidence()));
return adminJpaRepository.save(existing);
}
Controller
#RestController
#RequestMapping(
value = "super-admin",
produces = { MediaType.APPLICATION_JSON_VALUE }
)
public class SuperAdminController {
private final SuperAdminService service;
public SuperAdminController(SuperAdminService service) {
this.service = service;
}
#PutMapping("/update")
public Member updateProfile(#Valid #RequestBody UpdateProfileDto body){
Member superAdmin = service.updateProfile(body);
return superAdmin;
}
}
The password bug has been fixed(changes reflected in serviceImpl), but when I run the code it returned Duplicate entry 'ijava#gmail.com-111803918380' for key 'member.email_address_phone_number_uq' email, and the phone number is set as a unique constraint in the member table, how can I bypass this?
You have few options, depending on your exact use case.
Extract existing password, using unique property in UpdateProfileDto, email looks like it can do the job.
Pseudocode:
Member existing = repository.findByEmail;
Member superAdmin = mapper.map(body, Member.class);
superAdmin.setPassword(existing.getPassword());
Set a dummy value for password, to be updated later on.
superAdmin.setPassword("dummy-password");
Make the column nullable in database.

How to write query in springboot?

I am making a project where when a user login he will get a mail otp.I have successfully made the login page and also I am sending otp to the user mail address. Now I also want to validate the otp for that I have already created a otp column in database. But I can't figure out how to store the generated otp in the table.
Here is my code.
EmailSenderService class :
public class EmailSenderService {
#Autowired
private JavaMailSender mailSender;
public void sendMail(String toEmail,
String subject,
String body) {
SimpleMailMessage message=new SimpleMailMessage();
message.setFrom("growthgreek#gamil.com");
message.setTo(toEmail);
message.setText(body);
message.setSubject(subject);
mailSender.send(message);
System.out.println("message sent .....");
}
}
OtpEmailController Class:
#Controller
public class OtpEmailController {
#Autowired
private EmailSenderService emailService;
Random random = new Random(1000);
#PostMapping("/send-otp")
public String sendOtp(#RequestParam("email") String email) {
int otp = random.nextInt(999999);
String subject = "OTP from session-handling-proj By Harshit";
String toEmail = email;
String body = "<h1> OTP = " + otp + "</h1>";
this.emailService.sendMail(toEmail, subject, body);
return ("success");
}
Repository Class :
#Repository
#Transactional
public interface SessionHandlingRepository extends JpaRepository<SessionHandling, Integer>{
#Query(value="Select * from session_handling where email= :email",nativeQuery =true)
public SessionHandling findByEmailId(#Param("email")String email);
#Query(value="Select * from session_handling where email= :email AND password= :password",nativeQuery =true)
public SessionHandling findByEmailIdAndPassword(#Param("email")String email, #Param("password")String password);
}
Entity Class :
#Entity
public class SessionHandling {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private String email;
private String password;
private String cpassword;
private static final long OTP_VALID_DURATION = 5 * 60 * 1000; // 5 minutes
#Column(name = "one_time_password")
private String oneTimePassword;
#Column(name = "otp_requested_time")
private Date otpRequestedTime;
Where and how to write the query for saving the otp in database?
You can do it with your repository, first inject the repository in your service :
#Autowired
private SessionHandlingRepository sessionHandlingRepository;
you can then create an instance of your entity at the desired location (add getter and setter to the entity first):
SessionHandling sessionHandling = new SessionHandling();
sessionHandling.setName("theName");
// call Other setters ...
you can use the following repository method to save the entity in the database :
sessionHandlingRepository.save(sessionHandling);

How to make relational mapping between entities using mongoDB in sprinboot?

I am trying to create a springboot application using MongoDB and a Rest controller and connect objects together using DBRef instead of classic Jpa annotations like OneToMany etc. The purpose is to print all the bookmarks for a specific account. The list of bookmarks is found by the username but it seems that it doesn't work.
These are my classes:
#Document
public class Account {
#DBRef
private Set<Bookmark> bookmarkSet = new HashSet<>();
#Id
private String id;
#JsonIgnore
private String username;
private String password;
public Account(String username, String password) {
this.username = username;
this.password = password;
}
public void setBookmarkSet(Set<Bookmark> bookmarkSet) {
this.bookmarkSet = bookmarkSet;
}
public String getId() {
return id;
}
}
#Document
public class Bookmark {
#DBRef
#JsonIgnore
private Account account;
#Id
private String id;
private String uri;
private String description;
public Bookmark(Account account, String uri, String description) {
this.account = account;
this.uri = uri;
this.description = description;
}
public Account getAccount() {
return account;
}
public String getId() {
return id;
}
public String getUri() {
return uri;
}
public String getDescription() {
return description;
}
}
repositories:
public interface AccountRepository extends MongoRepository<Account, Long> {
Optional<Account> findOneByUsername(String username);
}
public interface BookmarkRepository extends MongoRepository<Bookmark, Long> {
Collection<Bookmark> findByAccountUsername(String username);
}
And RestController:
#RestController
#RequestMapping("/{userId}/bookmarks")
public class BookmarkRestController {
private final AccountRepository accountRepository;
private final BookmarkRepository bookmarkRepository;
#Autowired
public BookmarkRestController(AccountRepository accountRepository, BookmarkRepository bookmarkRepository) {
this.accountRepository = accountRepository;
this.bookmarkRepository = bookmarkRepository;
}
#RequestMapping(value = "/{bookmarkId}", method = RequestMethod.GET)
Bookmark readBookmark(#PathVariable String userId, #PathVariable Long bookmarkId) {
this.validateUser(userId);
return bookmarkRepository.findOne(bookmarkId);
}
#RequestMapping(method = RequestMethod.GET)
Collection<Bookmark> readBookmarks(#PathVariable String userId) {
this.validateUser(userId);
return this.bookmarkRepository.findByAccountUsername(userId);
}
private void validateUser(String userId) {
this.accountRepository.findOneByUsername(userId).orElseThrow(() -> new UserNotFoundException(userId));
}
}
After I run the application I get this error:
Invalid path reference account.username! Associations can only be pointed to directly or via their id property!
I'm not sure you have the right schema design. I assume you've modeled you objects based on a relational database type model, where the data is normalised and data is split across multiple tables, with relationships captured using Ids. With MongoDB you can structure and store your data with the heirarchy simply contained in within the one document.
So in your example the Bookmark would not be a Document itself, but would be a sub document of the Account. Remove the #Document annotation from the Bookmark object, and the #DBRef annotations, and simply store the Bookmarks within the Account document.
This would give you a schema more like this:
{
"_id": 1,
"bookmarkSet": [
{
"uri": "http://www.foo.com",
"description": "foo"
},
{
"uri": "http://www.bar.com",
"description": "bar"
}
],
"username": "John",
"password": "password"
}
*Note: if you make the bookmarks sub documents you can remove the _id member from the Bookmark object
The best design will depend on how many bookmarks you expect each account to have. If its only a few bookmarks then what I suggested would work well. If you have thousands then you might want to structure it differently. There are lots of articles about schema design in NoSQL database. This one covers the options for embedding subdocuments quite well:
http://blog.mongodb.org/post/87200945828/6-rules-of-thumb-for-mongodb-schema-design-part-1

Spring Data MongoDB - Audit issue with unique index

If the createdBy references to a document with unique indexes, it fails throwing dup key error.
AbstractDocument.java
public abstract class AbstractDocument implements Auditable<User, String> {
#Version
private Long version;
#Id
private String id;
private User createdBy;
private DateTime createdDate;
private User lastModifiedBy;
private DateTime lastModifiedDate;
}
User.java
#Document(collection = "users")
public class User extends AbstractDocument {
private String name;
private String surname;
#Indexed(unique = true)
private String username;
}
Book.java
#Document(collection = "books")
public Book extends AbstractDocument {
private String title;
}
Now, I have a script (Spring Batch) which initializes the db with some books. The script defines the auditor this way:
#Configuration
#EnableMongoAuditing
public class MongoConfig {
#Bean
public AuditorAware<User> auditorProvider() {
return new AuditorAware<User>() {
#Override
public User getCurrentAuditor() {
User auditor = new User();
auditor.setUsername("init-batch");
auditor.setName("Data initializer");
auditor.setSurname("Data initializer");
return auditor;
}
};
}
}
The script in somewhere does (for each book I need to persist) bookRepository.save(book)
The first book is persisted, but the second one throws:
nested exception is com.mongodb.DuplicateKeyException: Write failed with error code 11000 and error message 'E11000 duplicate key error index: mydb.books.$createdBy.username dup key: { : "init-batch" }'
Why? The unique index is for users collection, why is it checked for audit references?

BasicPropertyAccessor:167 - IllegalArgumentException in class in Struts

I'm working on online exam project using struts spring and hibernate. While submitting the values from registration.jsp, i'm trying to insert those details in two different tables by using one table's primary key as foreign key in another table. But i could able to save only values in one table( says primary key table). But i could not able to save details in another table. In console log, i could see the follwing exception,
2013-09-17 18:16:39 INFO RegistrationAction:188 - Entering Into SaveUserDetails()
2013-09-17 18:16:39 INFO class:25 - Entering Into saveUserRegistration()
2013-09-17 18:16:39 INFO class:13 - Entering Into UserRegistrationDAO
Hibernate: insert into user_details (first_name, last_name, email, password, gender, dob, phone, experience) values (?, ?, ?, ?, ?, ?, ?, ?)
2013-09-17 18:16:39 INFO RegistrationAction:214 - Entering Into setUserAddress()
2013-09-17 18:16:39 INFO class:25 - Entering Into saveUserRegistration()
2013-09-17 18:16:39 INFO class:13 - Entering Into UserRegistrationDAO
2013-09-17 18:16:39 ERROR BasicPropertyAccessor:167 - IllegalArgumentException in class: onlineexam.beans.UserDetails, getter method of property: user_id
Sep 17, 2013 6:16:39 PM org.apache.catalina.core.ApplicationDispatcher invoke
RegistrationAction.java
public String SaveUserDetails() {
String forward = "success";
try {
logger.info("Entering Into SaveUserDetails()");//Log Information
UserDetails s = new UserDetails();
s.setFirst_name(getFirst_Name());
s.setLast_name(getLast_Name());
s.setEmail(getEmailid());
s.setPassword(getPassWord());
s.setGender(getGender());
s.setDob(getDateofbirth());
s.setPhone(getPhoneNo());
s.setExperience(getUser_experience());
userRegistrationService.saveUserRegistration(s);
Set<UserAddress> address = new HashSet<UserAddress>(0);
setUserAddress(address);
logger.info("SuccessFull:Exiting from SaveUserDetails()");//Log Information
} catch (Exception ex) {
forward = "error";
}
return forward;
}
public void setUserAddress(Set<UserAddress> address) throws Exception {
logger.info("Entering Into setUserAddress()");
UserAddress ad = new UserAddress();
ad.setAddr_line1(getAddr_line1());
ad.setAddr_line2(getAddr_line2());
ad.setAddr_line3(getAddr_line3());
ad.setCity(getCity());
ad.setZipcode(getZipcode());
ad.setState(getState());
ad.setCountry(getCountry());
address.add(ad);
userRegistrationService.saveUserRegistration(ad);
logger.info("SuccessFull:Exiting from setUserAddress()");//Log Information
}
}
UserRegistrationDAO.java
public class UserRegistrationDao extends HibernateDaoSupport {
private static Logger logger = Logger.getLogger("UserRegistrationDao.class");
public UserRegistrationDao() {}
public UserDetails saveUserRegistration(UserDetails s) throws Exception {
logger.info("Entering Into UserRegistrationDAO");
return (UserDetails)getHibernateTemplate().merge(s);
}
}
UserRegistrationService.java
public class UserRegistrationService {
private UserRegistrationDao userRegistrationDao;
private static Logger logger=Logger.getLogger("UserRegistrationService.class");
public void init() throws Exception {}
public UserRegistrationDao getUserRegistrationDao() {
logger.info("Entering into getUserRegistrationDao()");//Log information
return userRegistrationDao;
}
public void setUserRegistrationDao(UserRegistrationDao userRegistrationDao) {
this.userRegistrationDao = userRegistrationDao;
logger.info("Entering Into setUserRegistrationDao()");//Log Information
}
public UserDetails saveUserRegistration (UserDetails user) throws Exception {
logger.info("Entering Into saveUserRegistration()");//Log Information
return userRegistrationDao.saveUserRegistration(user);
}
}
UserDetails.java
public class UserDetails {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#OneToMany (mappedBy="user_details")
private int user_id; //primary key
private String first_name;
private String last_name;
private String email;
private String password;
private String gender;
private int dob;
private int phone;
private float experience;
//getters and setters created
UserAddress.java
public class UserAddress extends UserDetails {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int address_id; //primary key
#ManyToOne(fetch=FetchType.EAGER, targetEntity=UserDetails.class)
#JoinColumn(name="user_id")
private int user_id;
private String addr_line1;
private String addr_line2;
private String addr_line3;
private String city;
private int zipcode;
private String state;
private String country;
//getters and setters created
What are you doing in UserAddress doesn't constitute a many to one association from UserAddress to UserDetails. Your are only adding the user_id to UserDetails class.
What you have to do instead:
#ManyToOne(fetch=FetchType.EAGER, targetEntity=UserDetails.class)
#JoinColumn(name="user_id")
UserDetails userDetails;
public UserDetails getUserDetails() {
return userDetails;
}
public void setUserDetails() {
this.userDetails = userDetails;
}
I don't understand why UserAddress extends UserDetails? I recommend you to read more about hibernate associations.

Categories