I am currently playing around with SpringBoot and wanna create a little API that allows me to save and fetch persistent data. I can't find the right solutions online, thats why I asking here. Creating an entity and with that a database table was very easy to do, and so was the implementation of the POST and GET request.
I have a very basic idea here. I have a table of players. Each of those players can participate in a foosball game, taking one of the four possible positions.
One player can have multiple games. A game can have one player (For each field).
Because of how easy everything was till the entity relation, I would assume that SpringBoot can automatically fetch the right player based on the id, that is inside of the POST request. But at the moment my application just throws an error, because my players are null and I made them non-nullable.
Do I need to manually fetch the player from the PlayerRepository and append them on the game object or do I miss some annotations? What would be the best practice to pull of those four API calls?
That how I would design my POST request:
{
"attackBlackPlayerId": 1,
"attackYellowPlayerId": 2,
"defenseBlackPlayerId": 3,
"defenseYellowPlayerId": 4,
"black_won": true
}
#Entity
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
private String email;
#CreationTimestamp
#Column(nullable = false)
private LocalDateTime creationDate;
}
#Entity
public class Game {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#CreationTimestamp
#Column(nullable = false)
private LocalDateTime playDateTime;
#ManyToOne(optional = false)
#JoinColumn(nullable = false)
private Player attackBlackPlayer;
#ManyToOne(optional = false)
#JoinColumn(nullable = false)
private Player defenseBlackPlayer;
#ManyToOne(optional = false)
#JoinColumn(nullable = false)
private Player attackYellowPlayer;
#ManyToOne(optional = false)
#JoinColumn(nullable = false)
private Player defenseYellowPlayer;
#Column(nullable = false)
private boolean blackWon;
}
#RestController
public class API {
#Autowired
private PlayerRepository playerRepository;
#Autowired
private GameRepository gameRepository;
#GetMapping("/players")
public #ResponseBody Iterable<Player> getPlayers() {
return playerRepository.findAll();
}
#PostMapping("/player")
public #ResponseBody Player addPlayer(#RequestBody Player player) {
return playerRepository.save(player);
}
#GetMapping("/games")
public #ResponseBody Iterable<Game> getGames() {
return gameRepository.findAll();
}
#PostMapping("/game")
public #ResponseBody Game addGame(#RequestBody Game game) {
return gameRepository.save(game);
}
}
Your Lord Tkay
The #OneToMany and #ManyToOne annotations have fields which must be correctly initialized if you want that the mapping works as expected.
Example :
#Entity
public class Employee {
#Id
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "employee")
private List<Email> emails;
}
#Entity
public class Email {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "employee_id")
private Employee employee;
}
Related
I wrote a few service methods. When I implement CommandLineRunner to my SpringApplication and try to execute service methods in run methods I can see they're working.
I mean like this approach, I can reach the data via service methods. So I can say yes my services are working right.
public void run(String... args) throws Exception {
postService.getAllPost()
.stream()
.forEach(entry -> System.out.println(entry.getTitle()));
}
But When I try to use my controllers via postman. For the User class, everything is fine.
public List<UserDto> getAllUsers() {
return userRepository.findAll()
.stream()
.map(userMapper::toDto)
.collect(toList());
}
#GetMapping("/get")
public ResponseEntity<List<UserDto>> getAll() {
return ResponseEntity.ok(userService.getAllUsers());
}
But Comment and Post are not working.
The methods are almost same. I was wondering is it about my entities relation? Because in User entity I just have 3 different field which is Long, String , String . But the other entities are related with each others. With OneToMany relational. And I dont trust about my business plan. I mean post class includes user and comment object. Comment class includes user object etc.
This is controller method.
#GetMapping("/getAll")
public ResponseEntity<List<CommentDto>> fetchAll() {
return ResponseEntity.ok(commentService.getAllComments());
}
This is service method.
public List<CommentDto> getAllComments() {
return commentRepository.findAll().stream()
.map(commentMapper::toDto)
.collect(Collectors.toList());
}
I have three different entity class.
Which is
#Entity
#Table(name = "user_table")
public class User{
#Id
private Long id;
private String userName;
private String password;
}
#Entity
#Table(name = "post_table")
public class Post {
#Id
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id" , nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private User user;
private String title;
#Lob
#Column(columnDefinition = "text")
private String text;
}
#Entity
#Table(name = "comment_table")
public class Comment {
#Id
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "post_id" , nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Post post;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id" , nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private User user;
#Lob
#Column(columnDefinition = "text")
private String text;
I have two classes, Role and Permission with a ManyToMany relationship between them. My problem is that each relationship has some extra data that comes with it therefore I believe I need to create an intermediary class to store these extra data, so that is the RolePermission class.
This is basically what I have, the parameter and domain are the extra data that are required for each relationship.
Here is the code I have right now for my classes.
Role.java
#Entity
#Table(name = "Sec_Role")
#DynamicUpdate
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String description;
private String name;
// This is another relationship which is working just fine (because there are no intermediary data needed.
#ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER)
private Set<Group> groups;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "role")
private List<RolePermission> permissions = new ArrayList<RolePermission>(0);
...Standard getters and setters and constructor
Permission.java
#Entity
#Table(name = "Sec_Permission")
public class Permission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "permission")
private List<RolePermission> roles = new ArrayList<RolePermission>(0);
...Standard getters and setters and constructor
RolePermission.java
#Entity
#Table(name = "Sec_Role_Permission")
#DynamicUpdate
public class RolePermission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int domain;
private String parameter;
#Column(updatable = false, insertable = false)
private int role_id;
#Column(updatable = false, insertable = false)
private int permission_id;
#ManyToOne(fetch = FetchType.LAZY )
#JoinColumn(name = "role_id", nullable = false)
private Role role;
#ManyToOne(fetch = FetchType.LAZY )
#JoinColumn(name = "permission_id", nullable = false)
private Permission permission;
...Standard getters and setters and constructor
Every class has a standard Repository like so
#RepositoryRestResource(path = "roles")
public interface RoleRepository extends CrudRepository<Role, Integer> {
}
Now this code works fine for reading data and relationships, my problem is that I cannot figure out what I am suppose to do or what end point to call to add/modify/delete a relationship between Role and Permission.
Currently if I call /roles/11/permissions I will get this back:
{
"_embedded": {
"rolePermissions": []
},
"_links": {
"self": {
"href": "http://localhost:8887/api/v1/roles/11/permissions"
}
}
}
How can I add a permission to this role?
I tried executing a POST request to /roles/11/permissions with the following JSON body and I got a 204 No Content response. This basically means success but then when I do a GET request to /roles/11/permissions I do not see permission with ID 1 there so it did not work.
{
"domain": 0,
"parameter": "Some param",
"role_id": 11,
"permission_id": 1
}
Since your mapping is itself an Entity you can model your API based on the Resource ( in this case RolePermission). Basically when you would want to provide an API to add rolepermission
Some thing like
http://localhost:8887/api/v1/rolepermission
POST
{
"roleid":"xxxxx"
"permissionid":"xxxxxx"
"parameterid":"xxxxxx"
"domain":"xxxxx"
}
So, i'm trying to get my Student Object to my androidClient from server, but i have ifinity loops. If i use #JsonBackReference/#JsonManagedReference or #JsonIgnore i won't get Object like in case of infinity loop, so i have a question how to do this? Here my classes:
Student.java
#Entity
#Table(name ="student")
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#GenericGenerator(name="increment", strategy = "increment")
private int id_student;
#Column(name = "studentName", nullable = false)
private String studentName;
#Column(name = "formOfAducation")
private String formOfAducation;
#ManyToOne
#JoinColumn(name ="id_group")
#JsonBackReference
private Group group;
#Column(name = "studentCourse")
private String studentCourse;
#Column(name = "studentSpecializatio")
private String studentSpecializatio;
#Column(name = "studentBookName")
private String studentBookName;
#Column(name = "studentGender")
private String studentGender;
public Student(){
}
//getter-setters
}
Group.java
#Entity
#Table(name = "party")
public class Group {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#GenericGenerator(name="increment", strategy = "increment")
private int id_group;
#Column(name = "groupName", nullable = false)
private String groupName;
#OneToMany(targetEntity = Student.class, mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonManagedReference
private List<Student> student;
public Group(){
}
On client i have the same classes, and when i'm trying to pass only id from group i cant deserialize it on client. Hope you'll help me. Or there's no way to do this, so how can i edit Student object from client?
There are two possible ways.
The first one is to create DTO objects and initialize them from the entities. Thus you manually stop on desired level without loops.
The second way is to unproxy the entities to break lazy collection loading which leads to the loops.
I'm creating a MySQL database as followed :
database design
the Country and Province tables are pre-filled with data. I have the application running and can get stuff no problem, and also the join table person_has_address works when getting.
however, when I insert data using post I want to be able to set the ID of the province, and let spring data jpa just add that number to add_pro_id in the Address table. For example, when I post the following json:
{ "firstName":"bilbo", "lastName":"baggings", "address":{"street":"streetName", "streetNum":3, "zipcode":"1337GG", "city":"TheCity", "province":{"name":"aProvinceName"}} }
jpa should see that aProvinceName exists and grab that id and add that to add_pro_id.
Now it just insert aProvinceName as new value in province and add the new id to add_pro_id.
The person class:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="per_id")
private int id;
#Column(name="per_name")
private String firstName;
#Column(name="per_surname")
private String lastName;
#Column(name="per_birth_date")
private String birthDate;
#Column(name="per_fax")
private String fax;
#Column(name="per_phone")
private String phone;
#Column(name="per_email")
private String email;
#OneToOne(optional = false, cascade = CascadeType.ALL)
#JoinTable(name="person_has_address", joinColumns = {#JoinColumn(name="pha_per_id", referencedColumnName = "per_id")}, inverseJoinColumns = {#JoinColumn(name="pha_add_id", referencedColumnName = "add_id")})
private Address address;
// getters and setters
This is the person repository:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(#Param("name") String name);
}
This is the address class:
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="add_id")
private int id;
#Column(name = "add_street")
private String street;
#Column(name="add_street_num")
private int streetNum;
#Column(name="add_zip")
private String zipcode;
#Column(name="add_city")
private String city;
#JoinColumn(name="add_pro_id", referencedColumnName = "pro_id")
#ManyToOne(optional=false, cascade = CascadeType.ALL)
private Province province;
// getters and setters
Province class:
#Entity
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="pro_id")
private int id;
#Column(name="pro_name")
private String name;
#ManyToOne
#JoinColumn(name="pro_cou_id")
private Country country;
// getters and setters
And lastly country class:
#Entity
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="cou_id", insertable = false, updatable = false)
private int id;
#Column(name="cou_name", insertable = false, updatable = false)
private String name;
// getters and setters
I've tried adding insertable = false and updatable = false, but the application then just inserts NULL values in my database. I've also tried working with #primarykeyjoins, but to no success.
if anyone knows how I should tackle this problem I would much appreciate it!
Thanks in advance.
So i have a user and a client.The user can have multiple clients.But json cannot return a value user.
So i did something like this :
#Column
private Integer fkIdUser ;
But i'm new to hibernate and i'm wondering if this is the right way of doing this. Or do i need to use a class with a many to one annotation but how would i do this with json ?
User class
public class User {
public static User globalUser;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_user")
private Integer id;
#Column(unique = true)
private String email;
Then the Client class
#Entity
#Table(name ="tbl_clients")
#Access(value = AccessType.FIELD)
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_client")
private Integer id;
/* This works
#Column
private Integer fkIdUser ;
*/
// This does not
#ManyToOne
#JoinColumn(name = "fk_id_user")
private User user;
I'm using this function in the ClientController to store the client to the database
#RequestMapping(value = "/addclient",method = RequestMethod.POST)
public void addClient(#RequestBody Client client) {
clientDao.save(client);
}
You have to use the same name of the column in the #JoinColumn name. Since you are using fkIdUser as the variable and it works, I suppose this is your column name. Then your mapping should be like this:
#ManyToOne
#JoinColumn(name = "fkIdUser")
private User user;