I've been trying to test some simple GET and POST request methods, using Postman and curl through command line.
For some reason, when I try to create a json file and send it through Postman, it saves all the data into the first variable.
I have no idea what's going on. The frontend will deliver everything through JSON files, so if this isn't working, then I want to fix it before finishing up my controller.
Here's my pharmaceutical model:
#Entity
#Table(name = "pharmaceuticals")
public class Pharmaceutical {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "genericName")
private String genericName;
#Column(name = "brandNames")
private ArrayList<String> brandNames;
#Column(name = "strength" )
private String strength;
#Column(name = "quantity")
private Integer quantity;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.MERGE,
CascadeType.REFRESH
})
#JoinTable(name = "pharm_commonuses",
joinColumns = { #JoinColumn(name = "pharmaceutical_id") },
inverseJoinColumns = { #JoinColumn(name = "commonUse_id") })
private Set<CommonUse> commonUses = new HashSet<>();
public Pharmaceutical() {}
public Pharmaceutical(String genericName, ArrayList<String> brandNames, String strength,
Integer quantity) {
this.genericName = genericName;
this.brandNames = brandNames;
this.strength = strength;
this.quantity = quantity;
}
//getters and setters
Here's my controller:
#CrossOrigin(origins = "http://localhost:8081")
#RestController
#RequestMapping("/api")
public class PharmaceuticalController {
#Autowired
PharmaceuticalRepository pharmRepository;
CommonUseRepository comRepository;
#GetMapping("/pharmaceuticals")
public ResponseEntity<List<Pharmaceutical>> getPharmaceuticals(#RequestParam(required = false) String title){
List<Pharmaceutical> pharms = new ArrayList<Pharmaceutical>();
pharmRepository.findAll().forEach(pharms::add);
return new ResponseEntity<>(pharms, HttpStatus.OK);
}
#PostMapping("/pharmaceuticals")
public ResponseEntity<Pharmaceutical> createPharmaceutical(#RequestBody String generic, ArrayList<String> brands, String strength, Integer quant, ArrayList<String> common){
Pharmaceutical newPharm = new Pharmaceutical(generic, brands, strength, quant);
for (String name: common) {
CommonUse com = new CommonUse(name);
comRepository.save(com);
newPharm.getCommonUses().add(com);
}
pharmRepository.save(newPharm);
return new ResponseEntity<>(newPharm, HttpStatus.CREATED);
}
}
Any help would be great!
This is your problem:
#RequestBody String generic
You are saying that the body that comes in, should be placed into this string.
You should build an object representation of the body you are sending in and change it to:
#RequestBody PharmaceuticalRequest generic
and then remove all the other input parameters in the createPharmaceutical function.
Reference:
https://www.baeldung.com/spring-request-response-body##requestbody
Related
I am building a simple weather app and in the api call I have query params where I can define what sensors to include and what weather data properties to include. When I have the query like SELECT w FROM WeatherData w ... the api response shows the key value pairs
But if I do a query like SELECT w.temperature, w.humidity FROM WeatherData w ... it just displays the values and not the properties.
How can I have it that the response includes the keys temperature and humidity? It's not just those, I could query to have just the temperature. But how do I include the keys in the response?
Entity
#Getter
#Setter
#Entity(name = "WeatherData")
#Table(name = "weatherdata")
public class WeatherData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
private float temperature;
private float humidity;
#Column(name = "wind_speed")
private float windSpeed;
#ManyToOne
#JoinColumn(name = "sensor_id")
#NotEmpty(message = "Weather data needs to be linked to a sensor")
private Sensor sensor;
#CreationTimestamp
#Column(nullable = false, updatable = false)
private LocalDateTime timestamp;
}
Controller
#Path("/weather")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class WeatherDataController {
#Inject
WeatherDataService service;
#POST
public Response postWeatherData(#NotNull #Valid WeatherData weatherData) {
WeatherData createdWeatherData = service.saveWeatherData(weatherData);
return Response
.status(Response.Status.CREATED)
.entity(createdWeatherData)
.build();
}
#GET
#Path("/{weatherDataId}")
public Response getSpecificWeatherData(#PathParam("weatherDataId") Long weatherDataId) {
WeatherData data = service.getWeatherDataById(weatherDataId);
return Response
.ok(data)
.build();
}
#GET
public Response getWeatherData(
#QueryParam("sensor") List<String> sensorIds,
#QueryParam("metric") List<String> metric,
#QueryParam("statistic") String statistic,
#QueryParam("dateStart") String dateStart,
#QueryParam("dateEnd") String dateEnd
) throws Exception {
ObjectNode response = service.getWeatherData(sensorIds, metric, Statistics.valueOf(statistic.toUpperCase()), dateStart, dateEnd);
return Response
.ok(response)
.build();
}
}
Just do not select certain values if you want to see full datasets.
If you need certain Key Value Sets returned you should probably edit your API Endpoint accordingly. Maybe you can build a new Entity Type that your Endpoint can return.
I have an error about "findAll" when I use JPA inheritage tables.
I what make the Json result like this ["asdf" : "adf", "asdf" : "asdf"]
but the return values are like [com.example.model.AccountEntity#57af674a]
Controller
#RequestMapping(value = "/getMyInfoall", produces = MediaType.APPLICATION_JSON_VALUE)
public String getMemberall(#RequestBody JSONObject sendInfo) throws IOException {
List user = UserService.findAll();
JSONObject result = new JSONObject();
result.put("data", user);
return result.toJSONString();
}
Service
public List findAll() {
List users = UserRepository.findAll();
return users;
}
Repository
#Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
}
Entity
#Entity(name = "Users")
#Inheritance(strategy = InheritanceType.JOINED)
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int userkey;
#Column(nullable = false, unique = true)
private String id;
#Column(nullable = false, length = 50)
private String name;
#Column(nullable = false)
private String password;
#Column(nullable = true)
private String email;
}
#Entity(name = "Account")
public class AccountEntity extends UserEntity{
#Column(nullable = false, unique = true)
private String accountno;
#Column(nullable = true)
private String accountname;
#Column(nullable = false)
private int accountpw;
#Column(nullable = false)
private long balance;
}```
I would highly recommend to use Spring's default HTTPMessageConverters, e.g. Jackson for JSON.
Building a JSON-array from a List
But you can also use JSON.org's light-weight library like guided on JSON-java README:
convert the List to an array, e.g. UserEntity[]
create a JSONArray from this Java array
return this JSON-array representation formatted as String, using method toString()
List<UserEntity> userList = // a list returned from your database/repo
UserEntity[] myArr = userList.toArray(new UserEntity[userList.size()]); // convert this to an array
// here simply follow the guide on JSON
JSONArray jArr = new JSONArray(myArr);
// return the JSON-array as string
return jArr.toString();
You should convert your UserEntity objects to a UserDto DTO that would then be returned in your Controller. Rely on Jackson instead of JSONObject managed and created by you.
public class UserDto {
private String id;
private String name;
}
You Service should do the mapping:
public List<UserDto> findAll() {
List<UserEntity> users = UserRepository.findAll();
return users.stream().map(user -> // your mapping logic to UserDto object);
}
And your Controller just needs to return it:
#RequestMapping(value = "/getMyInfoall", produces = MediaType.APPLICATION_JSON_VALUE)
public List<UserDto> getMemberall(#RequestBody JSONObject sendInfo) throws IOException {
return UserService.findAll();
}
You can do a similar thing with JSONObject sendInfo and replace it with an object of your own.
I have two entities, which are in a many to many relationship.
#Entity
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany(mappedBy = "rooms")
private Set<Team> teams;
}
#Entity
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany
#JoinTable(name = "teams_rooms",
joinColumns = #JoinColumn(name= "team_id"),
inverseJoinColumns = #JoinColumn(name = "room_id"))
private Set<Room> rooms;
}
To yield data, i have a repository for "Room" and "Team":
public interface RoomRepository extends CrudRepository<Room, Long> {
}
public interface TeamRepository extends CrudRepository<Team, Long> {
}
My goal is to request all rooms of a team, but prevent JPA from looping infinitely.
#RestController
#RequestMapping("....")
public class RoomController {
#Autowired
private RoomRepository roomRepository;
#GetMapping
public Iterable<Room> getAllRoomsOfTeam() {
final long exampleId = 1; //This is just a placeholder. The id will be passed as a parameter.
final var team = teamRepository.findById(exampleId);
return ResponseEntity.ok(team);
}
}
This is the result:
{
"id": 1,
"name": "Team1",
"rooms": [
{
"id": 1,
"name": "Room 1",
"teams": [
{
"id": 1,
"name": "Team 1",
"rooms": [
{
"id": 1,
"name": "Room 1",
"teams": [
Jackson will loop forever, until an exception occurs (Since the back reference also references the parent element, which will create a loop).
I already tried #JsonManagedReference and #JsonBackReference, but they are used for many to one relationships.
How do i stop Jackson from looping infinitely? I want to affect other repositories and queries as little as possible.
Your controller shoud not return entities ( classes with the annotation #Entity). As a best practice is to create another separate class with same attributes. This code has a little dupplication but it keeps all the layers clean. I also suggest to use #Service.
public class RoomDTO {
private String name;
private List<TeamDTO> teams = new ArrayList<>();
public RoomDTO() {
}
public RoomDTO(Room room) {
this.name = room.name;
for(Team team : room.getTeams()) {
TeamDTO teamDTO = new TeamDTO();
teamDTO.setName(team.getName);
teams.add(teamDTO);
}
}
}
public class TeamDTO {
List<RoomDTO> rooms = new ArrayList();
public TeamDTO() {
}
public TeamDTO(Team team) {
this.name = team.name;
for(Room room : team.getRooms()) {
RoomDTO roomDTO = new RoomDTO();
roomDTO.setName(team.getName);
rooms.add(roomDTO);
}
}
}
The controller should return this
#GetMapping
public Iterable<TeamDTO> getAllRoomsOfTeam() {
final long exampleId = 1;
final var team = teamRepository.findById(exampleId);
TeamDTO teamDTO = new TeamDTO(team);
return ResponseEntity.ok(teamDTO);
}
How to use DTOs in the Controller, Service and Repository pattern
Currently, there is cyclic dependency in your classes which is causing issues while converting objects to JSON. Please add #JsonIgnore annotation on rooms variable in your Team class as shown in below example:
import com.fasterxml.jackson.annotation.JsonIgnore;
#Entity
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany
#JoinTable(name = "teams_rooms",
joinColumns = #JoinColumn(name= "team_id"),
inverseJoinColumns = #JoinColumn(name = "room_id"))
#JsonIgnore
private Set<Room> rooms;
}
If you need a solution for bidirectional conversion then you can use JsonView annotation.
First of all you need to create JSON view profiles for Team and Room as shown in below example:
public class JsonViewProfiles
{
/**
* This profile will be used while converting Team object to JSON
*/
public static class Team {}
/**
* This profile will be used while converting Room object to JSON
*/
public static class Room {}
}
Use above created JSON view profiles in your entities as shown in below example:
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView({ JsonViewProfiles.Team.class, JsonViewProfiles.Room.class })
private long id;
#JsonView(JsonViewProfiles.Room.class)
#ManyToMany(mappedBy = "rooms")
private Set<Team> teams;
}
public class Team {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView({JsonViewProfiles.Team.class, JsonViewProfiles.Room.class})
private long id;
#ManyToMany
#JoinTable(name = "teams_rooms",
joinColumns = #JoinColumn(name= "team_id"),
inverseJoinColumns = #JoinColumn(name = "room_id"))
#JsonView(JsonViewProfiles.Team.class)
private Set<Room> rooms;
}
While converting your object to JSON please use these profiles as shown in below example:
#GetMapping
public String getAllRoomsOfTeam() {
final long exampleId = 1; //This is just a placeholder. The id will be passed as a parameter.
final Team team = teamRepository.findById(exampleId);
String result = new ObjectMapper().writerWithView(JsonViewProfiles.Team.class)
.writeValueAsString(team);
return result;
}
I am trying to add ManyToMany entity to my application. I created entity but cannot implement it.
Actor class
#Entity
#Table(name = "actor")
public class Actor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false, name = "actor_name")
private String actorName;
#ManyToMany(mappedBy = "actor", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Movie> movie = new HashSet<Movie>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getActorName() { return actorName; }
public void setActorName(String actorName) {
this.actorName = actorName;
}
public Set<Movie> getMovie() {
return movie;
}
public void setMovie(Set<Movie> movie) {
this.movie = movie;
}
}
In movie class I have
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "movie_actor",
joinColumns = {#JoinColumn(name = "movie_id")},
inverseJoinColumns = {#JoinColumn(name = "actor_id")}
)
Set<Actor> actor = new HashSet<Actor>();
........................
public Set<Actor> getActor () {
return actor;
}
public void setActor(Set<Actor> actor){
this.actor = actor;
}
I created my entity just like this but in MovieService;
Actor actor = ActorRepository.findByActorName(movie.getActor().getActorName());
movie.setActor(actor);
This part gives me error. movie.getActor().getActorName() method cannot find. Where do I need to look? In IDE it also says method getActorName and setActorName is never used. I am also adding my ActorRepository and ActorService to closer look to the problem.
ActorRepository
public interface ActorRepository extends JpaRepository<Actor, Integer> {
Set<Actor> findByActorName(String actorName);
}
ActorService
#Service
public class ActorService {
private ActorRepository actorRepository;
#Autowired
public ActorService(ActorRepository actorRepository) {
this.actorRepository = actorRepository;
}
public List<Actor> getAllActor() {
return actorRepository.findAll();
}
}
After adding ManyToMany I was using is as OneToMany entity. Services is works for OneToMany. How can I use them for ManyToMany? I need to add multiple actors to my movies. I couldn't find MVC projects for ManyToMany implementation.
You're invoking movie.getActor().getActorName() which basically does a getActorName() on a Set<Actor> object.
You're basically treating the relation as a ManyToOne instead of a OneToMany
You could use the following to fetch the first Actor of the Set
ActorRepository.findByActorName(movie.getActors().iterator().next().getActorName());
But then of course, you don't have all your Actor's names
What you could do is the following
public interface ActorRepository extends JpaRepository<Actor, Integer> {
Set<Actor> findByActorNameIn(List<String> actorName);
}
And invoke it that way
ActorRepository.findByActorNameIn(
movie.getActors()
.stream()
.map(Actor::getName)
.collect(Collectors.toList())
);
I'm currently working on system that consists of Java Web app and C# client app. Web app has Java Web Service, which has method that returns entity object of Program class:
#WebMethod(operationName = "getProgram")
public Program getProgram(#WebParam(name = "macAddress") String macAddress){
Device device = DeviceManager.getInstance().getDevice(macAddress);
if(device != null){
return device.getProgram();
}
return null;
}
This return object of type Program which has many properties and relations:
#Entity
#Table(name = "PROGRAM", schema = "APP")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Program.getProgramsByWeather", query = "SELECT p FROM Program p WHERE p.weather = :weather")})
public class Program extends DbEntity implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "LOGO_ID", referencedColumnName = "ID")
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch= FetchType.EAGER)
private Logo logo;
#JoinColumn(name = "WEATHER_ID", referencedColumnName = "ID")
#ManyToOne
private Weather weather;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "program", orphanRemoval = true)
private List<ProgramPlaylist> programPlaylistList = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, mappedBy = "program", orphanRemoval = true)
private List<ProgramTicker> programTickerList = new ArrayList<>();
#Column(name = "UPDATED")
private boolean updated;
public Program() {
}
public Program(String name, AppUser owner) {
super(name, owner);
}
public Logo getLogo() {
return logo;
}
public void setLogo(Logo logo) {
this.logo = logo;
}
public Weather getWeather() {
return weather;
}
public void setWeather(Weather weather) {
this.weather = weather;
}
public boolean isUpdated() {
return updated;
}
public void setUpdated(boolean updated) {
this.updated = updated;
}
#XmlElement
public List<ProgramPlaylist> getProgramPlaylistList() {
return programPlaylistList;
}
#XmlElement
public List<ProgramTicker> getProgramTickerList() {
return programTickerList;
}
#Override
public String toString() {
return "Program[ id=" + getId() + " ]";
}
}
Client can get this object and accessing some properties in client app like program.name, which it inherits from DbEntity, but when i try to call something like this:
program.logo.name
client throws NullReferenceException.
Same exception occurs when i try to iterate over the elements of programPlaylistList ArrayList.
I'm assuming that the object itself that is passed through to client isn't fully loaded.
How can i solve this problem, please help?!
EDIT
Ok, so I printed out XML response that client get from service and its populated correctly, but for some reason object fields aren't populated and are mostly null.
Why is this occurring?
Bye default, the fetch strategy for #OneToMany annotations is LAZY, have you tried specifying it to EAGER like in the #oneToOne field (fetch= FetchType.EAGER)?