How edit a pojo in spring java? - java

I want to edit a user, should I replace it with another user? Or should I just replace the attributes?
I have this controller
#PostMapping("/edit")
public ResponseEntity<User> editUser(#RequestBody User user) {
log.info("EDIT");
return new ResponseEntity<User>(userServiceImpl.editUser(user), HttpStatus.OK);
}
this my service:
public User editUser(User user) {
User owner = userRepository.findById(user.getId());
owner=user;
userRepository.save(user);
return owner;
}
the class User is
#Entity
#Table(name = "User")
public class User implements Serializable {
private static final long serialVersionUID = -3009157732242241606L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "name")
private String name;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
I need to do something like that, I mean do an insert query to the bd?
#Modifying
#Query("update User u set u.firstname = ?1, u.lastname = ?2 where u.id = ?3")
void setUserInfoById(String firstname, String lastname, Integer userId);

To make it clear.
Let think this simple.
You have userRepository.save(user);
What will it do?
If this object don't have an Id ~> Save it as new object
If this user HAD an ID, it will check OVERWRITE if the id is EXIT
So if you wanna Edit just SOME FIELDS, let do like convention
domain:port/user/{userId}/ (PUSH) and give some field need to change here.
~> Get the real obj in DB ~> update the object and do with SAVE.
/ ~> Or you can do the query update directly if you want

Just save updated user
public User editUser(User user) {
return userRepository.save(user);
}
It will be enough.

Here, this will ensure that no data is lost and a new record is not generated.
public User editUser(User user) {
User owner = userRepository.findById(user.getId());
if(user.getName != null){
owner.setName(user.getName);
}
if(user.getPassword != null){
owner.setPassword(user.getPassword);
}
if(user.getEmail != null){
owner.setEmail(user.getEmail);
}
return userRepository.save(owner);
}

Related

Child and Parent Database - Spring Boot

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} }

JPQL for finding if user has sent or recieved friend request from an other user

I have been developing simple social networking project, I was to implement User -> Friend relationship as AppUser.java:
#Data
#Entity(name = "users")
#NoArgsConstructor
#RequiredArgsConstructor
#EqualsAndHashCode(callSuper = false)
public class AppUser extends AuditModel {
#NonNull
private String userName;
#NonNull
#Column(unique = true, nullable = false)
private String userEmail;
private String userPassword;
#OneToMany( mappedBy="owner", cascade=CascadeType.ALL, orphanRemoval = true)
private List<Friend> friends = new ArrayList<>();
}
and Friend.java
#Data
#Entity(name = "friends")
#NoArgsConstructor
#RequiredArgsConstructor
#EqualsAndHashCode(exclude = {"owner"}) // to avoid StackOverflow
#ToString(exclude = {"owner"}) // // to avoid StackOverflow
public class Friend{
#NonNull
#EmbeddedId
private Key key = new Key();
#NonNull
#ManyToOne
#MapsId("ownerId")
private AppUser owner;
#NonNull
#ManyToOne
#MapsId("personId")
private AppUser person;
#NonNull
private boolean accepted;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
public static class Key implements Serializable {
private Long ownerId;
private Long personId;
}
}
If user sends friend request to an other user records be like:
At this point every thing is working fine except the case where I want to hide Add Friend button on profile page of the User who has sent me a friend request or I have sent him a friend request, I tried with the JPQL in FriendRepository.java as:
#Repository
public interface FriendRepository extends PagingAndSortingRepository<Friend, Long> {
#Query(value = "SELECT count(f) FROM friends f WHERE (f.owner.id = :id OR f.person.id = :id)")
Integer findPendingOrFriend(#Param("id") Long id);
}
and in Service I have:
public boolean findPendingOrFriend(Long id) {
// Find friends ids who/Whom friend requests are sent
return friendRepository.findPendingOrFriend(id) > 0;
}
in controller I have:
#GetMapping(value = "/{userId}/profile")
public String showProfile(
Model model,
#PathVariable(value = "userId") Long userId
) throws Exception {
// Send model
String pageTitle = "User " + Util.getLoggedInUser(userService).getUserFirstName() ;
model.addAttribute("pageTitle", pageTitle);
model.addAttribute("loggedInUser", Util.getLoggedInUser(userService));
model.addAttribute("profileUser", userService.findById(userId).orElseThrow(EntityNotFoundException::new));
// Check if pending or friend
boolean isPendingOrFriend = friendService.findPendingOrFriend(userId);
model.addAttribute("isPendingOrFriend", isPendingOrFriend);
return "user_profile";
}
but it does not work as expected, because it hides Add Friend button for a new user who have not sent any friend requests to any other user of the Application and when a friend request is sent it always shows Add Friend button on user profile page.
Question is how can I re-write
#Query(value = "SELECT count(f) FROM friends f WHERE (f.owner.id = :id OR f.person.id = :id)")
Integer findPendingOrFriend(#Param("id") Long id);
so that it will work as desired above.
Keeping in mind the scenario I would rather go with the following:
#Query(value = "SELECT count(f) FROM friends f WHERE (f.owner.id = :oId AND f.person.id = :pId) OR (f.person.id = :oId AND f.owner.id = :pId)")
Integer findPendingOrFriend(#Param("oId") Long oId, #Param("pId") Long pId);
And in Service:
public boolean findPendingOrFriend(Long oId, Long pId) {
// Find friends ids
return friendRepository.findPendingOrFriend(oId, pId) > 0;
}
and in Controller:
#GetMapping(value = "/{userId}/profile")
public String showProfile(
Model model,
#PathVariable(value = "userId") Long userId
) throws Exception {
AppUser loggedInUser = Util.getLoggedInUser(userService);
// Send model
String pageTitle = "User " + loggedInUser.getUserFirstName() ;
model.addAttribute("pageTitle", pageTitle);
model.addAttribute("loggedInUser", loggedInUser);
model.addAttribute("profileUser", userService.findById(userId).orElseThrow(EntityNotFoundException::new));
// Check if pending or friend
boolean isPendingOrFriend = friendService.findPendingOrFriend(loggedInUser.getId(), userId);
model.addAttribute("isPendingOrFriend", isPendingOrFriend);
return "user_profile";
}
hope this works as desired.
Your query will not work, because you check that owner id or person id which not correct,
you should check both ids for the visitor id and owner's page id
#Query(value = "SELECT count(f) FROM friends f WHERE ((f.owner.id = :id1 AND f.person.id = :id2) OR (f.owner.id = :id2 AND f.person.id = :id1))")
Integer findPendingOrFriend(#Param("id1") Long id1, #Param("id2") Long id2);

JPA: Why is ID of selected object null?

In my JPA application, I am using the following SELECT statement to retrieve a user from the database:
TypedQuery<AppAdmin> query = entityManager.createQuery("SELECT a FROM AppAdmin a WHERE a.username=:username", AppAdmin.class);
query.setParameter("username", username);
AppAdmin user = query.getSingleResult();
However, the user's id field is always null, even though I checked that the field is not null in the database. What could be the cause of this behaviour?
The following shows my AppAdmin class:
#Entity
#Table(name = "u_userdetails_admin")
public class AppAdmin extends BasicUser {
/**
*
*/
private static final long serialVersionUID = -41356272870596876L;
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(name = "id", columnDefinition = "BINARY(16)")
private UUID id;
protected AppAdmin(){}
public AppAdmin(
#JsonProperty("id") UUID id,
#JsonProperty("username") String username,
#JsonProperty("password") String password,
#JsonProperty("registrationDate") LocalDate registrationDate,
#JsonProperty("locked") boolean locked,
#JsonProperty("enabled") boolean enabled,
#JsonProperty("activationKey") String activationKey,
#JsonProperty("email") String email) {
super(id, username, password, registrationDate, locked, enabled, activationKey, email, new ArrayList<String>());
}
}
This solves the problem: AppAdmin extends BasicUser and I defined an id field in both classes. The inheritance strategy is set to InheritanceType.JOINED, and removing the id defintion in AppAdmin causes the IDs to be loaded again. However, I am not sure if this is the real cause of the problem, as it used to work before until it just stopped working at some point.

How to use hibernate lazy loading with column that can be null

This is my entity:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.MERGE)
#JoinColumn(name = "id_city")
private City city;
//...
}
In my Repository I have:
public interface UserRepository extends JpaRepository<User, Long>{
#Query("SELECT u FROM User u JOIN FETCH u.city")
public List<User> findAllUserForApi();
}
If there are any cities in table, findAllUserForApi() shows me full information about user:
[{"id":1,"name":"John","surname":"Pillman","city":{"id":1,"name":"New York"}]
If there are no cities, I want to get at least [{"id":1,"name":"John","surname":"Pillman","city":null]
But I've got nothing: []
Help me please.
Given that you are already using a custom query, the simplest solution would be a LEFT JOIN FETCH:
#Query("SELECT u FROM User u LEFT JOIN FETCH u.city")
This way all users will be loaded regardless of whether they have a city or not; and for those who have a city, it'll be available by user.getCity().
Why you write custom query here. You dont need.
Firstly you have to follow general convention:
#ManyToOne(fetch = FetchType.LAZY, cascade=CascadeType.MERGE)
#JoinColumn(name = "CITY_ID")
private City city;
...
And here JPA shows all information related with User.
public interface UserRepository extends JpaRepository<User, Long>{
public List<User> findAll();
}
It looks like you are trying to use Lazy Loading with a predefined Query, I don't think this is going to work.
See, the JOIN FETCH in your query state the following:
Get all the Users which has u.City
So if you don't have a u.City for a user, the return would be empty.
More info on Join and Fetch
What you really want is the following:
public User findUserByID(Long userId)
{
Session session = sessionFactory.getCurrentSession();
User user = (Users) session.createCriteria(User.class).add(Restrictions.idEq(userId)).uniqueResult();
// this will force SQL to execute the query that will join with the user's city and populate
// the appropriate information into the user object.
Hibernate.initialize(user.geCity());
return user;
}
If the u.City is NULL, it will return a NULL. while the User object contains data.
Or in your case Find all users :
public List<User> findUserByID(Long userId)
{
Session session = sessionFactory.getCurrentSession();
List<User> users = (List<User>) session.createCriteria(User.class);
// this will force SQL to execute the query that will join with the user's city and populate
// the appropriate information into the user object.
for (User user : users)
Hibernate.initialize(user.geCity());
return user;
}
Note:
I did not test the code, this is pseudo so you may want to change some of it.
source

JPA delete bidirectional entity

I am having some problems with JPA. I am new at this topic so my question maybe is really stupid, but i hope some of you could point me to the right direction.
I have Project and User entity. Every user can have as many projects assign to him as it can.
I created the relationship bidirectional
User OneToMany -> Project,
Project ManyToOne -> User
My problem is that if i want to delete a user i want all the projects to be deleted as well, but i receive an error at that point:
Internal Exception: java.sql.SQLIntegrityConstraintViolationException:
DELETE on table 'USER_DATA' caused a violation of
foreign key constraint 'PROJECT_USERNAME' for key (Test Use1312r1).
The statement has been rolled back.
Error Code: -1
Call: DELETE FROM USER_DATA WHERE (USERNAME = ?)
bind => [1 parameter bound]
My User entity looks like this:
#Entity
#Table(name="USER_DATA", uniqueConstraints = #UniqueConstraint(columnNames = {"USERNAME", "link"}))
public class User implements Serializable {
#Column(name="USERNAME")
#Id
#NotNull
private String name;
#Column(name="USERROLE")
#Enumerated(EnumType.STRING)
private UserRole role;
private String password;
private String link;
// Should be unique
private String session;
#OneToMany(mappedBy="user", cascade=CascadeType.ALL)
private Collection<Project> projects;
My Project Entity like this:
#Entity
#Table(name="PROJECT")
#XmlRootElement
public class Project implements Serializable {
#Id
private int id;
private String name;
private String description;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="START_DATE")
private Date beginDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="END_DATE")
private Date endDate;
#ManyToOne
#JoinColumn(name="USERNAME", nullable=false,updatable= true)
private User user;
And my BL:
public User getUser(String userName) throws NoDataFoundException {
EntityManager em = DbConnection.getInstance().getNewEntity();
try {
User user = em.find(User.class, userName);
if (user == null) {
throw new NoDataFoundException("User is not found in the DB");
}
return user;
} finally {
em.close();
}
}
public void deleteUser(String userName) throws ModelManipulationException {
EntityManager em = DbConnection.getInstance().getNewEntity();
try {
User userToBeDeleted = getUser(userName);
em.getTransaction().begin();
userToBeDeleted = em.merge(userToBeDeleted);
em.remove(userToBeDeleted);
em.getTransaction().commit();
} catch (Exception e) {
throw new ModelManipulationException(
"Error in deleting user data for username" + userName
+ "with exception " +e.getMessage(),e);
}
finally{
em.close();
}
}
Thanks in advance guys.
after the merge call, are there any Projects in userToBeDeleted.projects? I suspect there are none, which prevents any from being deleted. Cascade remove can only work if you populate both sides of bidirectional relationships, so check that when you associate a user to a project, you also add the project to the user's project collection.

Categories