Objectify not returns new data - java

I'm using Ojectify to store datastore, I have an entity that when I keep it if the changes are saved, but in the browser to display it sometimes shows me the previous data and sometimes the new data.
#Entity
public class BrandDto {
#Id
private Long id;
#Index
private String name;
#Index
private List<Ref<UserDto>> users;
#Index
private Integer numeroFollowers;
getters and setters .....
}
It happens in users and numeroFollowers fields.
I update this data as follows:
UserDto user = ofy().load().type(UserDto.class).id(p_userId).now();
if (user != null) {
BrandDto brand = ofy().load().type(BrandDto.class).id(p_brandId).now(); //get(p_brandId);
List<UserDto> users = brand.getUsers() != null ? brand.getUsers() : new ArrayList<UserDto>();
if (!users.contains(user)) {
users.add(user);
}
brand.setUsers(users);
brand.setNumeroFollowers(users.size());
ofy().save().entity(brand).now();
return true;
} else {
return false;
}
And I read as follows:
List<BrandDto> brands = ofy().load().type(BrandDto.class).list();
Other query that I use:
UserDto user = ofy().load().type(UserDto.class).id(p_userId).now();
Ref<UserDto> ref = Ref.create(user);
List<BrandDto> brands = ofy().load().type(BrandDto.class).filter("users", ref).list();

While get-by-key operations (like load().type(...).id(...)) are strongly consistent by default, queries are eventually consistent.
Here's more information: https://cloud.google.com/developers/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/

Related

How to add data in bidirectional manytomany mapping?(ITS NOT A NORMAL PROCESS OF ADDING DATA)

public class Movies {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int movie_id;
#Column(unique = true, length = 255)
private String movie_name;
private String movie_language;
#ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
private Set<Actors> actors;
private String movie_genre;
//Getter-Setters
}
public class Actors {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int actor_id;
private String actor_name;
private String actor_industry;
#ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL,mappedBy = "actors")
Set<Movies> movies;
//Getter-Setters
}
/*This is where my logic resides*/
I have two entity Movies and Actors which has ManyToMany mapping.
If I add a movie with Actors,I am able to see that in databse with additional table which maps Movies and Actors.
Now suppose If I'm entering another new movie with same actor,It gives me an error that Actor already exist(Here actor_name in Actors and movie_name in Movies is unique),
How to add a movie in db such that It should enter new movie in Movies table but If actor already exist in Actors table It should simply map new Movie with already existing actor.
For example:First enter this
{
"movie_name":"Titanic",
"movie_language":"English",
"release_year":"1997",
"movie_genre":"Romance",
"actors":[
{
"actor_name":"Leonardo",
"actor_industry":"Hollywood"
}
]
}
Now I want to enter second movie as:
{
"movie_name":"Once upon a time in hollywood",
"movie_language":"English",
"release_year":"2019",
"movie_genre":"Drama",
"actors":[
{
"actor_name":"Lenardo",
"actor_industry":"Hollywood"
},
{
"actor_name":"Brad",
"actor_industry":"Hollywood"
}
]
}
:::::This is How I add new movie:::::::
public void addMovie(Movies movie) {
Movies tempMovie = movieRepository.findByName(movie.getMovie_name());
if (tempMovie != null) {
Set<Actors> actors = new HashSet<Actors>();
actors = movie.getActors();
for (Actors actors2 : actors) {
tempMovie.getActors().add(actors2);
}
movie = tempMovie;
movieRepository.save(movie);
} else {
System.out.println("############################In else");
Set<Actors> actors = movie.getActors();
for (Actors actors2 : actors) {
System.out.println(actors2.getActor_name());
Actors actorTemp = actorRepository.findByName(actors2.getActor_name());
// System.out.println("Id::::"+actorTemp.getActor_id());
if (actorTemp != null) {
System.out.println("Actor is there " + actors2.getActor_name());
System.out.println("Before" + actorTemp.getMovies());
actorTemp.getMovies().add(movie);
System.out.println("After:" + actorTemp.getMovies());
movie.getActors().add(actorTemp);
System.out.println("Added");
movieRepository.save(movie); //If I remove this line,then there's no error,but also no changes in DB.....without this line my new movie won't be persisted.
} else {
System.out.println("Actor is new");
movieRepository.save(movie);
}
}
}
}
I think you are looking for something like a bridge-table or join-table containg the identifiers for the movies and actors. This blog explains pretty good what you need to implement for this szenario. In order to avoid errors because of duplicate records in your DB get familiar with the difference between JPA's merge vs. persist (I'm assuming that you're using JPA due to the annotations).
I hope this helps, happy coding!

Update multiple users by passing list of User ids

I am trying to update some user information by passing List of User-Ids as parameter
i want to update isActive field of User fo which i am passing the user ids.
Below is my controller
#PutMapping
#ResponseStatus(HttpStatus.OK)
#RequestMapping("/UserUpdate")
public ResponseEntity<?> updateUsers(List<Long> userIds) {
**userService.updateUsers(userIds);**
return ResponseEntity.ok(200);
}
updateUsers() is a method in my Service where i have to write the logic
I tried something like below but it's not working
public void updateUsers(List<Long> userIds) {
List<Users> userList= userRepository.findAll();
for (Long i : userIds) {
for ( Users user : userList)
{
if(userRepository.findById(i) != null)
{
user.setIsActive(9L);
user.setUserName("Update Test");
}
my dto
public class UserDto {
private List<Users> userList;
private String appName="Users Project";
// getters and setters removed for brevity
And my Users entity class
#Entity
#Table(name="USERS")
public class Users {
#Id
#Column(name="USER_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
#Column(name="NAME")
private String userName;
#Column(name="ACTIVE")
private Long isActive;
// getters and setters removed for brevity
Alternatively you can use the following code
#Modifying
#Query("update Users u set u.isActive = ?1, u.userName = ?2 where u.userId in ?3")
void updateUsers(Long isActive, String userName, List<Long> userId);
Add this code in your userRepository and use the method.
public void updateUsers(List<Long> userIds) {
for (Long i : userIds) {
User user = userRepository.findById(i);
if(user != null){
user.setIsActive(9L);
user.setUserName("Update Test");
// call your update method here (this is not stated in your code)
}
}
}

findRecord in Google CloudDatastore with Objectify

I want to use Objectify to query Google Cloud Datastore. What is an appropriate way to find a record based on a known key-value pair? The record is in the database, I verified this by Google's Datastore viewer.
Here is my method stub, which triggers the NotFoundException:
#ApiMethod(name="getUser")
public User getUser() throws NotFoundException {
String filterKey = "googleId";
String filterVal = "jochen.bauer#gmail.com";
User user = OfyService.ofy().load().type(User.class).filter(filterKey, filterVal).first().now();
if (user == null) {
throw new NotFoundException("User Record does not exist");
}
return user;
}
Here is the User class:
#Entity
public class User {
#Id
Long id;
private HealthVault healthVault;
private String googleId;
public User(String googleId){
this.googleId = googleId;
this.healthVault = new HealthVault();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public HealthVault getHealthVault() {
return healthVault;
}
public void setHealthVault(HealthVault healthVault) {
this.healthVault = healthVault;
}
public String getGoogleId() {
return googleId;
}
public void setGoogleId(String googleId) {
this.googleId = googleId;
}
}
I think it fails because of transaction. You need to make a transctionless call like:
User user = OfyService.ofy().transactionless().load().type(User.class).filter(filterKey, filterVal).first().now();
More info about transactions on App Engine:
https://cloud.google.com/appengine/docs/java/datastore/transactions
https://github.com/objectify/objectify/wiki/Transactions
EDIT
Your object needs #Index annotation. It will add field to datastore index. Only properties that are in the index can be searchable. Filter method is one of them.
#Id
Long id;
#Index
private HealthVault healthVault;
#Index
private String googleId;
P.S. delete your object with googleId jochen.bauer#gmail.com and write it again to database after you updated your entity. And objectify will find it.
First add #Index in your fields model. I didn't see filterVal as an email in your model. Even so, to get the entity based in your filterVal assuming that is googleId is the field of your entity.
User user = OfyService.ofy().load().type(User.class).filter("googleId", filterVal).now();
And so if your filterKey is the id of your entity.
User user = OfyService.ofy().load().key(Key.create(User.class, filterKey)).now();

detached entity passed to persist when Composite key used with entity

i'm getting error like
org.springframework.dao.InvalidDataAccessApiUsageException: detached
entity passed to persist: com.websopti.wotms.entity.Project; nested
exception is org.hibernate.PersistentObjectException: detached entity
passed to persist: com.websopti.wotms.entity.Project
i have Composite key join on entity basically i have two entity one is Project and one is User and i have created composite key join between them by making another Entity called ProjectUser following are classes
User
public class User extends BaseEntity<Long> implements UserDetails {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
...
#OneToMany(mappedBy="user",fetch=FetchType.LAZY)
private List<ProjectUser> userProjects;
...
getter and setters
}
Project
public class Project extends BaseEntity<Long> {
private static final long serialVersionUID = 7541005803335530236L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
...
#OneToMany(mappedBy="project",fetch=FetchType.LAZY, cascade=CascadeType.ALL)
private List<ProjectUser> projectUsers;
...
}
ProjectUser
#IdClass(CompositeKey.class)
public class ProjectUser extends BaseEntity<Long> {
private static final long serialVersionUID = 476483195548055916L;
#ManyToOne
#Id
#JoinColumn(name="user_id", referencedColumnName="id")
private User user;
#ManyToOne
#Id
#JoinColumn(name="project_id", referencedColumnName="id")
private Project project;
private ProjectRole role;
...
getter setter
}
CompositeKey
public class CompositeKey implements Serializable {
private static final long serialVersionUID = 2186028617883601307L;
private long user;
private long project;
public CompositeKey() {}
public CompositeKey(long user, long project) {
this.user = user;
this.project = project;
}
public boolean equals(Object object) {
if (object instanceof CompositeKey) {
CompositeKey pk = (CompositeKey)object;
return user == pk.user && project == pk.project;
} else {
return false;
}
}
...
getter setter
}
now when i create project at that time if i set List object and save then it works fine but when i wanted to update that project and set Modified List object that is manually created by code and set to project object. so now when i try to save this modified project object then i get error for "detached entity passed to persist".
i'm doing like following and saving this project object
my code for saving project object controller method like follow
#RequestMapping(value="/update", method=RequestMethod.POST)
public String updateProject(#ModelAttribute("project") Project project, HttpServletRequest request, RedirectAttributes redirectAttributes){
try {
project.setIpAddress(CommonUtils.getClientIpAddr(request));
Project oldProject = projectService.findById(project.getId());
List<ProjectUser> newProjectUsers = new ArrayList<ProjectUser>();
List<Integer> selectedIndexes = project.getSelectedRoleIndexes();
List<User> users = project.getTeam();
if(users != null && users.size() > 0){
for (User u : users) {
newProjectUsers.add(new ProjectUser(u, project, ProjectRole.getRole(selectedIndexes.get(users.indexOf(u)))));
}
}
List<ProjectUser> oldProjectUsers = oldProject.getProjectUsers();
for (ProjectUser projectUser : new ArrayList<>(oldProjectUsers)) {
if(!users.contains(projectUser.getUser())){
/*disable all task for user*/
//taskService.disableUserTaskForProject(projectUser.getUser(), oldProject);
/*send remove member form project mail*/
//projectEmailService.sendProjectTeamMemberRemoveEmail(projectUser.getUser(), oldProject);
oldProjectUsers.remove(projectUser);
}else{
ProjectUser pu = newProjectUsers.get(users.indexOf(projectUser.getUser()));
oldProjectUsers.remove(projectUser);
projectUser.setRole(pu.getRole());
oldProjectUsers.add(projectUser);
}
}
List<User> oldTeam = oldProjectUsers.stream().map(pu -> {return pu.getUser();}).collect(Collectors.toList());
for (ProjectUser projectUser : newProjectUsers) {
if(!oldTeam.contains(projectUser.getUser())){
/*send user add in project mail*/
//projectEmailService.sendProjectTeamMemberAddEmail(projectUser.getUser(), oldProject);
oldProjectUsers.add(projectUser);
}
}
//oldProjectUsers = entityManager.merge(oldProjectUsers);
//projectUserService.updateAllProjectUsers(oldProjectUsers);
/*for (ProjectUser projectUser : oldProjectUsers) {
entityManager.merge(projectUser);
}*/
project.setProjectUsers(oldProjectUsers);
//project = entityManager.merge(project);
project = projectService.update(project);
/*old team except admin*/
/*List<User> oldTeam = oldProject.getProjectUsers()
.stream()
.map(pu -> {return pu.getUser();})
.collect(Collectors.toList());
List<User> newTeam = project.getTeam()
.stream()
.filter(u -> u.getRole() != SystemRole.ADMIN)
.collect(Collectors.toList());
project = projectService.update(project);
for (User user : oldTeam) {
if(!newTeam.contains(user)){
disable all task for user
taskService.disableUserTaskForProject(user, project);
send remove member form project mail
projectEmailService.sendProjectTeamMemberRemoveEmail(user, project);
}
}
for (User user : newTeam) {
if(!oldTeam.contains(user)){
send user add in project mail
projectEmailService.sendProjectTeamMemberAddEmail(user, project);
}
}*/
} catch(Exception e) {
e.printStackTrace();
return "redirect:/user/UserDashboard";
}
redirectAttributes.addFlashAttribute("projectId",project.getId());
redirectAttributes.addFlashAttribute("fromUpdate", true);
return "redirect:/user/"+PageTemplate.userDashboard;
}
please help me i'm stuck here
first of all thanks hansnae for giving me confidence to work on that question because i have visited that question 3 times and go through that question also have thinked to apply that solution but not applied because of i'm not sure about that.
now that bidirectional relation ship issue is there in my case so i have applied that logic for modification in List object and it worked for me
JPA/Hibernate: detached entity passed to persist
but in that question remove object facility not worked for me because i have used composite key join in my case i haev to work extra to remove that object
when i try to save removed object and set null in related joined entity then my case it throws Exception
No part of a composite identifier may be null
so to remove that object from list i have to manually remove that object by repository delete query.
Thanks for help and hands up too this great community

Is there a way to get the session information from the Model in PlayFramework?

I create the app by PlayFramework 2.2 with Java.
When saving Model, I want to save the create_user_id / update_user_id as tracking information.
But, unable to retrieve the session information from Model of PlayFramework.
Currently, I pass the User entity from Controller, when save the entities.
because it is awful implementation, When you call Model.save() method to get a login user from the session in the Model side
I want to like us to save implementation.
Please tell me if you know the best practices.
public abstract class AbstractTrailModel extends Model {
public static final int INSERT = 1;
public static final int UPDATE = 2;
public static final int DELETE = 3;
#ManyToOne(cascade = CascadeType.ALL)
#Constraints.Required
public User createUser;
#ManyToOne(cascade = CascadeType.ALL)
#Constraints.Required
public User createUser;
#Constraints.Required
public Integer is_delete;
public void save(User loginUser){
if(create_time != null){
setTrailInfo(UPDATE, loginUser);
super.save();
}else{
setTrailInfo(INSERT, loginUser);
super.save();
}
}
public void setTrailInfo(int code, User loginUser){
switch(code) {
case INSERT:
createUser = loginUser;
case UPDATE:
case DELETE:
updateUser = loginUser;
}
if(code == DELETE){
is_delete = 1;
}else{
is_delete = 0;
}
}
}
As far as I known its only possible to get the session from a Controller.
If you need the User on the model classes, you need the pass the session or the User as an argument everywhere you need.

Categories