I am stuck here, i want to keep address of user as embedded document . But there,it is creating another table of address in database. so i want one table "user" and inside it,i want my embedded document like "address". what will be the domain design of user ?.
Now,my user domain is
class User{
String name;
Address adress;
}
My address domain is
class Address{
String city;
}
My controller is
#RestController
#RequestMapping("/api/v1")
public class UserController {
public Map<String, Object> saveUser(#RequestBody User user) {
userService.saveUser(user);
return ResponseHandler.generateResponse("User registerted successfully", HttpStatus.ACCEPTED, false, null);
}
}
My service is
public void saveUser(User user){
Address a = new Address();
a.setCity("delhi");
User user = new User();
user.setAddress(a);
userDao.save(user);
}
I want result is in database,not table of address separately.i am using spring-data-mongotemplate for this.how can i do this ?
user{
address:[{},{}] // multiple address could be
}
Maybe something like this?
#Document
public class User {
private String name;
private List<Address> addresses = new ArrayList<>();
// ctr, getters, setters...
public static class Address {
private String city;
public Address(String city) {
this.city = city;
}
// getters, setters...
}
}
In controller:
User.Address address = new User.Address("NY");
user.setAddress(address);
mongoTemplate.save(user);
Basically, parsing User from json should work:
{ name : "userName", addresses : [ { city : 'NY' } ] }
And you don't have to embed Address class into User class. Just add #Document on User and ebmed Address object in it.
Use #Document annotation in user, and both must implements Serializable.
Regards.
Related
I want to have a relation oneToOne between Person and Address. So my Entities will be (without getter and setter):
public class Address {
private Integer addressId;
private String streetAddress;
...getter and setter
}
public class Person {
private Integer personId;
private String name;
private Integer addressId;
...getter and setter
}
I have a mapper for Person and a mapper for Address:
#Mapper
public interface AddressMapper {
#Insert("Insert into address (streetAddress) values(#{streetAddress})")
#Options(useGeneratedKeys = true, flushCache = FlushCachePolicy.TRUE, keyProperty="addressId")
public Address saveAddress(Address address);
#Select("SELECT * FROM Address")
public List<Address> getAll();
}
#Mapper
public interface PersonMapper {
#Insert("Insert into person(name,addressId) values (#{name},#{addressId})")
public Integer save(Person person);
#Select("select * from Person ")
#MapKey("personId")
Map<Integer, Person> getAllPerson();
}
Then I use a Controller to generete a Person and associate an Address (it is just a simple example):
#GetMapping("/insert")
public Integer insertPerson() {
Person person = new Person("Nome1");
Address address = new Address("via test");
int addressId = addressMapper.saveAddress(address);
person.setAddressId(addressId);
return personMapper.save(person);
}
My problem is that addressMapper.saveAddress(address); return the number of addresses saved not the id. There is a way to make it return the ID of the address inserted using Java Configuration?
Otherwise is it possibile to use a Person entity in this way?
public class Person {
private Integer personId;
private String name;
private Address addressId;
...getter and setter
}
Github code (it is public so you can clone the repository if you want):
https://github.com/Mazzotta13/MyBatisExample
I was trying to use Spring Data JPA on Spring Boot and I kept getting error, I can't figure out what the problem is:
Unable to locate Attribute with the the given name [firstName] on
this ManagedType [com.example.h2demo.domain.Subscriber]
FirstName is declared in my entity class. I have used a service class with DAO before with different project and worked perfectly.
My Entity class (getters and setters are also in the class) :
#Entity
public class Subscriber {
#Id #GeneratedValue
private long id;
private String FirstName,LastName,Email;
public Subscriber(long id, String firstName, String lastName, String email) {
this.id = id;
this.FirstName = firstName;
this.LastName = lastName;
this.Email = email;
}
}
...
My Repository Class
#Component
public interface SubscriberRepository extends JpaRepository<Subscriber,Long> {
Subscriber findByFirstName(String FirstName);
Subscriber deleteAllByFirstName(String FirstName);
}
My Service Class
#Service
public class SubscriberService {
#Autowired
private SubscriberRepository subscriberRepository;
public Subscriber findByFirstName(String name){
return subscriberRepository.findByFirstName(name);
}
public Subscriber deleteAllByFirstName(String name){
return subscriberRepository.deleteAllByFirstName(name);
}
public void addSubscriber(Subscriber student) {
subscriberRepository.save(student);
}
}
And My Controller class:
#RestController
#RequestMapping("/subscribers")
public class SubscriberController {
#Autowired
private SubscriberService subscriberService;
#GetMapping(value = "/{name}")
public Subscriber findByFirstName(#PathVariable("name") String fname){
return subscriberService.findByFirstName(fname);
}
#PostMapping( value = "/add")
public String insertStudent(#RequestBody final Subscriber subscriber){
subscriberService.addSubscriber(subscriber);
return "Done";
}
}
Try changing private String FirstName,LastName,Email; to private String firstName,lastName,email;
It should work.
findByFirstName in SubscriberRepository tries to find a field firstName by convention which is not there.
Further reference on how properties inside the entities are traversed https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
The same problem was when i had deal with Spring Data Specifications (https://www.baeldung.com/rest-api-search-language-spring-data-specifications)
Initial piece of code was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("project_number"), "%" + projectNumber)
}
The problem was in root.get("project_number"). Inside the method, I had to put the field name as in the model (projectNumber), but I sent the field name as in the database (project_number).
That is, the final correct decision was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("projectNumber"), "%" + projectNumber)
}
After I change my entity class variables from capital letter to small letter for instance Username to username the method Users findByUsername(String username); is working for me now .
As per specification , the property names should start with small case.
...The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized)....
It will try to find a property with uncapitalized name. So use firstName instead of FristName and etc..
Here is my controller method:
// CREATE A USER
#PostMapping("/register")
public String createUser(
#RequestBody User user
) {
if (userService.userExists(user)) {
return "User already exists";
}
userService.saveUser(user);
return "Good job!";
}
UserServiceBean
#Service
public class UserServiceBean {
private UserRepository userRepository;
#Autowired
public UserServiceBean(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User saveUser(User user) {
return userRepository.save(user);
}
public boolean userExists(User user) {
if (userRepository.findByUsername(user.getUsername()) == null) {
return false;
}
return true;
}
And my interface repository:
UserRepository
public interface UserRepository extends CrudRepository<User, Long> {
// TODO: 29.01.17 Create a query to find all todos for logged in user
#Query("select td from User u inner join u.toDoItems td where u = :user")
public Iterable<ToDoItem> findAllToDosForLoggedInUser(#Param("user") User user);
public User findByUsername(String username);
}
And here is my User Entity (getters and setters ommited)
#Entity
#Table (name = "USERS")
public class User extends BaseEntity {
#Column(name = "USERNAME")
private String username;
// TODO: 28.01.17 Find a way to store hashed and salted pws in DB
#Column(name = "PASSWORD")
private String password;
#Column(name = "EMAIL")
private String email;
// user can have many ToDoItems
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private Set<ToDoItem> toDoItems;
// JPA demands empty constructor
public User() {}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
When I shoot JSON at my localhost:8080/register:
{
"username":"ss",
"password":"mkyong.com",
"email":"asdasd#wp.pl"
}
I get response Good job! so it works fine. But when I check my DB at localhost:8080/console it just has Test Table and new User is not added.
I've got my hibernate ddl setup in application.properties set:
# Console to H2 database to check data
spring.h2.console.enabled=true
spring.h2.console.path=/console
spring.jpa.hibernate.ddl-auto=create-drop
So, how do I update my code that it creates table USERS and save created user into that db? I'm going to change my db later on, just using H2 to check if my controllers work fine but it shouldn't matter here.
EDIT:
Here is my RepositoryConfiguration.java:
#Configuration
#EnableAutoConfiguration
#EntityScan(basePackages = {"com.doublemc.domain"})
#EnableJpaRepositories(basePackages = {"com.doublemc.repositories"})
#EnableTransactionManagement
public class RepositoryConfiguration {
}
EDIT2:
When I want to register the same User again (using same JSON) then it gives me "User already exists" resposne so it is already in the db... Why can't I see it then? Maybe I've got H2 somewhere else? Not in the basic /console or different port? How can I check this?
I think you're missing the transactional part of your service. Did you define a transaction manager in your spring context ?
If so, you need to add the annotation #Transactional into your service. For example :
#Service
public class UserServiceBean {
#Transactional
public User saveUser(User user) {
return userRepository.save(user);
}
}
I had to add:
spring.datasource.url=jdbc:h2:~/test
spring.datasource.driver-class-name=org.h2.Driver
to application.properties and it works great now. I just thought I don't need it becasue Spring will auto-configure it for me but apparently it doesn't.
Trying to play with JAX RS i want to implement CRUD operations with my data. First of all i want to get list of objects in json formate.
#Path("/users")
public class ListUsersRestController {
#GET
#Produces("application/json")
public List<User> getUsers(){
List<User> users = new ArrayList<>();
users.add(new User("Dean", "Winchester"));
users.add(new User("Sam", "Winchester"));
users.add(new User("Bobby", "Singer"));
return users;
}
#XmlRootElement
public class User {
#XmlElement(name="first-name")
private String firstName;
#XmlElement(name="last-name")
private String lastName;
public User(){
}
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
}
When i request my URI i always get 500 server error and there are no any errors in log file (I am using glassfish).
The problem is here:
#XmlRootElement
public class User {
...
}
Your User class now is an inner class of ListUsersRestController and it seems that JAXB fails to marshall inner classes (because they are more like an instance member of ListUsersRestController than a real class). Either externalize it to be a normal class or make it static:
#XmlRootElement
public static class User {
...
}
I have two models User and Address.
public class User extends Model{
public String name;
public List<Address> addresses;
}
public class Address extends Model{
public String street;
public String house;
}
I have a template to add a user.
#(form: Form[User])
#helper.form(action=routes.User.add())
{
<label>Add User<label>
#text(form("name"), label="Name")
<input type="submit" class="btn btn-primary" value="Save" />
}
In my controller I have the add method.
public static Results add()
{
Form<User> filledForm = userForm.bindFromRequest();
User user = filledForm.get();
}
I have skipped unnecessary information like the error handling.
Now how can I bind the addresses? I want to be able to add multiple addresses in the form.
In Play 1.1 I have seen things like POJO collections but I am using 2.3.
As mentioned in comment probably JavaScript and AJAX would be most comfortable solution, you can i.e. create an User as unactive and activate him only when at least one address is added later.
Other solution can be using custom class with fields from both (User and Address) so after validating all required fields you can create an User and associated Address.
Sample
User model:
public class User extends Model {
public String firstName;
public String lastName;
#OneToMany(mappedBy = "user")
public List<Address> addresses; // This field doesn't keep relations it informs that relation is kept in `user` field of `Address` model
}
Address model:
public class Address extends Model {
#ManyToOne
public User user;
public String street;
public String house;
}
UserAdmin controller:
public static class UserWithAddress { //This is our class for validating data for user and his `primary` address
// User's fields
#Required
public String firstName;
#Required
public String lastName;
// Address' fields
#Required
public String street;
#Required
public String house;
}
public static Result addUserWithAddress() {
Form<UserWithAddress> userForm = Form.form(UserWithAddress.class);
return ok(userWithAddressView.render(userForm));
}
public static Result saveUserWithAddress() {
Form<UserWithAddress> userForm = Form.form(UserWithAddress.class).bindFromRequest();
if (userForm.hasErrors()) {
return badRequest(userWithAddressView.render(userForm));
}
// form is valid, let's extract data from form, create and save both objects
UserWithAddress data = userForm.get();
User user = new User();
user.firstName = data.firstName;
user.lastName = data.lastName;
user.save(); // Save new user before creating new Address, cause we need a new ID to associate...
Address address = new Address();
address.user = user;
address.street = data.street;
address.house = data.house;
address.save();
return ok("Hooray you're registered now! And we know where you live ;)");
}
userWithAddressView.scala.html view
#(userForm: Form[controllers.UsersAdmin.UserWithAddress])
#main(title = "Input your basic data and address data") {
#helper.form(action=routes.UsersAdmin.saveUserWithAddress()){
#helper.inputText(userForm("firstName"))
#helper.inputText(userForm("lastName"))
#helper.inputText(userForm("street"))
#helper.inputText(userForm("house"))
<input type="submit" value="Save"/>
}
}
So I built it using the #helper.repeat. The best example is in the samples provided by Play. It's called forms.
For a quick reference, the description can be found here - https://www.playframework.com/documentation/2.2.x/JavaFormHelpers
#helper.inputText(userForm("name"))
#helper.repeat(userForm("emails"), min = 1) { emailField =>
#helper.inputText(emailField)
}
For the address example:
#(form: Form[User])
#helper.form(action=routes.User.add())
{
<label>Add User<label>
#text(form("name"), label="Name")
#helper.repeat(form("addresses"), min= 1){ address =>
#helper.inputText(address("street"))
#helper.inputText(address("house"))
}
<input type="submit" class="btn btn-primary" value="Save" />
}
Validation works as normal if you add the required annotaion over street and house. The only thing you need to do is add the #Valid (javax.validation.Valid) annotation over the list of addresses.
#Valid
public List<Address> addresses;
For adding and removing addresses, the sample is the best to look through.