I'm afraid I have a small problem with my project with Spring and MongoDB.
I have saved a user, which is shown under Email,Name and another object(workplace).
private String email;
private String name;
private WorkPlace workPlace;
I need the possibility to change the Workplace. I have not found anything reasonable in the Internet for an update. Only a "save" method to update the object.
The problem is, if I want to update the object, he takes the user, changes the Workplace and creates a new object. But I don't need a new object. The object is unique by its email address, because it only exists once.
I need only to change the Workplace
#PostMapping("/update")
public User update(#RequestParam String email, #RequestBody Workplace workplace ) {
User u = repository.findByEmail(email);
u.setWorkplace(workplace);
return repository.save(u);
}
I have set the repository as follows:
import org.springframework.data.mongodb.repository.MongoRepository;
#Repository
public interface ModelRepository extends MongoRepository<User, String> {
public User findByEmail(String email);
}
You need to have an #Id annotated property at your User class. Otherwise Spring Data MongoDB does not find an existing identifier and will do an insert of the object.
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document
#Document(collection="users")
class User {
#Id
private String email;
private String name;
private Workplace workplace;
}
If an entity is set, then a save instead of an insert is done.
Related
I've recently tried to implement Spring Security into my web store project to distinguish between single users. Websites are working properly except there is one issue which I can't track to resolve. I have object called Customer within User class. Customer object has fields like id, balance, etc., and User has OneToOne relationship to Customer, so I can have single object for credentials and foreign key to specifics of user - his first name, last name, balance, owned products, etc.
I also have Product class which has ManyToOne relationship with Customer. It has its' own id, productCost, etc.
I'm using Spring MVC to take care of proper URL dispatching. When some action is taken, I'm using #AuthenticationPrincipal annotation to get currently logged Customer (through foreign key in User) and modify data regarding Customer linked with that foreign key.
When I modify Customer data through #AuthenticationPrincipal in controller, changes are immediate and they show up on website. But when I try to modify data through some DAO, for example by searching for Customer through id or try to get Customer that owns Product from Product getter (ManyToOne has reference to owning Customer), changes are not immediate. Database updates itself immediately and properly, like in first case, but collections in code and website state are not changed until I logout and login again - that's when data is updated. I suspect it may be due to fact that updating UserDetails updates data directly for currently logged user but then - how may I achieve same effect for Customer found by id?
Snippets of code:
Users.java:
#Entity
#Table(name="users")
public class Users {
#Id
#Column(name="username")
private String username;
#Column(name="password")
private String password;
#Column(name="enabled")
private boolean isActive;
#OneToMany(mappedBy="user")
private Set<Authorities> authorities;
#OneToOne
#JoinColumn(name="customer_id")
private Customer customer;
Product.java:
#Entity
#Table(name="product")
public class Product {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String productName;
#Column(name="description")
private String productDescription;
#Column(name="category")
private String productCategory;
#Column(name="cost")
private int productCost;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="owner_id")
private Customer productOwner;
Customer.java:
#Entity
#Table(name="customer")
public class Customer {
//Class fields
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="balance")
private int balance;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="email")
private String email;
#OneToMany(mappedBy="productOwner", fetch=FetchType.EAGER)
private List<Product> ownedProducts;
Piece of controller code:
#Autowired
CustomerService customerService;
#Autowired
ProductService productService;
/*(...)*/
#GetMapping("/showOffer/{offerId}")
public String getOffer(#PathVariable int offerId, Model theModel, #AuthenticationPrincipal MyUserDetails user) {
Product retrievedProduct = productService.findById(offerId);
if (user.getCustomer().getBalance() >= retrievedProduct.getProductCost())
{
Customer retrievedProductOwner = retrievedProduct.getProductOwner();
/* This is where changes aren't applied immediately and I need to logout and login to process them. */
retrievedProductOwner.setBalance(1000);
/* This is where changes are immediately shown and Java collections are updated: */
user.getCustomer().setBalance(user.getCustomer().getBalance()-retrievedProduct.getProductCost());
/* Code below is an attempt to force immediate changes by updating collections directly from database - but that approach doesn't work */
productService.delete(retrievedProduct.getId());
retrievedProduct.getProductOwner().getOwnedProducts().clear();
retrievedProduct.getProductOwner().setOwnedProducts(productService.listOwnerProducts(retrievedProduct.getProductOwner()));
}
else {
System.out.println("Insufficient funds!");
}
return "redirect:/home";
TL:DR
I use UserDetails object in controller and I am also using DAO for Customer used as foreign key in UserDetails. Using UserDetails directly updates data and everything works fine, using DAO doesn't make changes until I logout and login.
as far as i understand your changes are only commited when you log out .
just try to synchronize and commit any modification at the right time and it would be safer that you manage sessions and transactions at the same time so you don't get any sort of incoherence when you do that. then tell me about the results .
Check whether CTRL+F5 in your browser (force cache clearance) updates your data similarly to logging out and back in. If so, it's a question of cached information. (this and (3) may occur at the same time)
Alternatively ... or perhaps complementarly ... your data fetch reqeust may be called before the database update/commit operation is completed. If so, it should become evident if you run distinct update and show routines. i.e. turn A into B, then into C, and you'd get something like B when you're expecting C... A instead of B... etc.
Lastly, depending on how you set up your back end, it is possible that you only populate whatever form you use for the front end exactly once, instead of dynamically querying the database whenever you access that form.
I want to map Java POJO to MongoDB and implement CRUD operations. I follow manual https://mongodb.github.io/mongo-java-driver/3.11/driver/getting-started/quick-start-pojo/ and all seems fine but one Person property is MongoDB dependent:
public final class Person {
private ObjectId id;
private String name;
private int age;
private Address address;}
This is org.bson.types.ObjectId id. This makes my domain layer dependent on MongoDB, and this actually what I would not call a POJO at all. Instead of ObjectId I would like to have String or other Java core class like Long or something like that. It could could be a kind of getter/setter too. How can I achieve this?
I tried to remove id from Person
package com.mongo_demo.domain;
public final class Person {
private String name;
private int age;
private Address address;}
and use this as my domain object, while to operate with MongoDB in DAO I will use child class:
package com.mongo_demo.mongo_domain;
public final class Person extends com.mongo_demo.domain.Person {
private ObjectId id;
}
Obviously my domain class now not have dependencies on MongoDB, but still lacks String id and no way to have getter method for it, as ObjectId id attribute is in child class.
I not sure is it fine to not have access to id value in my services code, because I could need to call delete by id operation, otherwise I will have to create my own object unique identifier, in addition to ObjectId id attribute, which will be natural key with consequent drawbacks.
PS No getter-setter methods shown, as I use Lombok #Data annotations instead.
I have one simple class
#Entity
#Table(name="user")
public class User implements Serializable {
#Id
#GeneratedValue
private Integer Id;
#Length(min = 5, message = "Username must be at least 5 characters long.")
#Column(name="username",nullable=false,unique=true)
private String userName;
#ManyToMany(cascade= {CascadeType.PERSIST},fetch=FetchType.EAGER)
#JoinTable(name="user_user_profile")
private Set<UserProfile> userProfile = new HashSet<>();
}
And second class:
#Entity
#Table(name = "user_profile")
public class UserProfile {
#javax.persistence.Id
#GeneratedValue
private int Id;
#Column(name = "type", nullable = false, unique = true)
#Enumerated(EnumType.STRING)
private UserProfileType type = UserProfileType.USER;
}
public enum UserProfileType {
USER("USER"),
ADMIN("ADMIN");
}
I'm using Spring MVC and Spring Secuirty with Hibernate. Is there any way to on start of the app make every possible entry in UserProfile Entity (there is only two)? Do I have to get UserProfile from database (via TypedQuery or EntityManager.find() ) and then add it to the User to not make any exceptions?
The enum items are static in your application, so I wouldn't try to make automatic changes in the database. Adding a new record is trivial, but removing an item that is already referenced may need individual care. These values are essential for your application, so I think they should be included in your SQL scripts.
If you are using DB versioning tools such as Flyway or Liquibase, add/remove records of the user_profile table in the migration scripts. They can be configured to run the migrations before your application (and Hibernate) starts, so the application will always see the correct data.
You can add a application start up event and persist the user profiles. You can delete all the user profiles before the application shut down as well. But I wouldn't recommend this as I assume the UserProfiles wouldn't change frequently. If that is the case, you are better off preloading the user profiles via some sql script as suggested in the other answer. If you really want to do it via app, the safest way would be to delete before the app gets shut down. Following is the sample snippet. I assume you are using spring-data-jpa and provided the snippet.
#Component
public class AppStartedListener implements ApplicationListener<ContextRefreshedEvent> {
#Autowired
private UserProfileRepository repository;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
for(UserProfileType userProfileType: UserProfileType.values()) {
UserProfile up = new UserProfile(userProfileType);
repository.save(up);
}
}
}
#Component
public class AppStoppedListener implements ApplicationListener<ContextClosedEvent> {
#Autowired
private UserProfileRepository repository;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repository.deleteAll();
}
}
public interface UserProfileRepository extends CrudRepository<UserProfile, Integer> {
}
So I added method to dao layer:
#Transactional
#EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
UserProfile user=new UserProfile();
em.persist(user);
UserProfile admin=new UserProfile();
admin.setType(UserProfileType.ADMIN);
em.persist(admin);
}
And now, before adding new User i just use HQL to get persistent UserProfile object that I can add to my User. Altough it works I will probably try to load it from some sort of *.sql file since I had to add method metioned above to the Dao layer interface (because of interface type proxy) and I don't like it to be honest.
I have been playing around with Spring Data and MongoDB and have a question about limiting the amount of data for certain queries. I've seen adding custom queries within the MongoRepository, however I haven't seen any examples of limiting the amount of data and returning classes that are basically a subset of larger classes.
For instance I have a User class that has several fields, but I also want to create a UserShort class that has a subset of the fields in the User class. For instance UserShort would only contain the id and firstName / lastName / email fields, rather than everything.
I've seen I can specify/limit the fields that are returned, but can I have those returned into a different class? At the moment the UserShort will return null unless I specify the User class instead, but the fields will be limited to the ones I specify. Wasn't sure if this is possible at all? I realize the User class below isn't huge, but it's the concept I'm after.
A user interface:
public interface Users {}
Subset class:
public class UserShort implements Users {
#Id
private String id;
private String firstName;
private String lastName;
#Indexed(unique = true)
private String email;
//getters / setters
}
Full class:
#Document
public class User implements Users {
#Id
private String id;
private String firstName;
private String lastName;
private String username;
private String password;
private Date dob;
private Status status;
#Indexed(unique = true)
private String email;
private Gender gender;
private String locale;
private Date registerDate;
#DBRef
private List<UserAddress> addresses;
public User(){
addresses = new ArrayList<UserAddress>();
}
//getters / setters
}
And the repository interface:
public interface UserRepository extends MongoRepository<Users, String> {
public User findByEmail(String email);
#Query(value="{ 'email' : ?0 }", fields="{ 'firstName' : 1, 'lastName' : 1}")
public UserShort findUserShortByEmail(String email);
}
As long as the return type of the query method is assignable to the managed domain type (Users in your case) we will prefer the return type to determine the collection to run the query against. Thus, in your case we'd execute the query against userShort instead of users which is why you do not get any results. That behavior is in place to support storing inheritance hierarchies into different collections.
If you switched to User as the domain type for the repository, things should work exactly as expected. This would also have the benefit of preventing clients from handing UserShort instances to the save(…) method which will wipe out properties contained in User but not in UserShort. Here's the final repository interface declaration.
interface UserRepository extends MongoRepository<User, String> {
User findByEmail(String email);
#Query(value="{ 'email' : ?0 }", fields="{ 'firstName' : 1, 'lastName' : 1}")
UserShort findUserShortByEmail(String email);
}
P.S.: #byte-crunch outlined in the comments that this currently only works for collections of projections but not for returning single instances. This has been reported and fixed in DATAMONGO-1030 and will be available in 1.5.4 and 1.6.0 GA.
im new in hibernate.
I would like to build a simple autentication / login system in java with hibernate.
So let's say, i have a class User
public class User {
private int id;
private String username;
private String passwordHash;
...
}
Now i have a DAO to store a new User, and to get all users (as a list). Now im wondering, if its possible to get a list of users without the passwordHash field (for security reason)?
It would be nice if it was a question of configuration.
An other idea would be to split the User class into
public class User {
private int id;
private String username;
...
}
public class UserWithPassword extends User {
private String passwordHash;
...
}
So i could use UserWithPassword to store a new user into the database and use
the User class to query the list of all users (without password).
Any other suggestion?
Your split class won't work because you have to link a class to Hibernate.
Your DAO doesn't have to return the class itself. You can write an HQL query such:
select username
from User
See?
Then your DAO would have a method like public Collection getUserNames()
you can use
java.util.List temp = hibernateTemplate.find("select u from user u ");
you can take all user from temp;
but if you want authenticate,you can use spring security,i suggest