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.
Related
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'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
I use GraphQL-SPQR Library
The problem is "Validation error of type SubSelectionRequired: Sub selection required for type Timestamp"
Maybe there is expression in query for timestamp
or format in Entity
{"query":
"{findUserPointByUserId(userId:73){rowNum userAccountPointUserId totalPoint pointTypeDescription point userAccountCreatedDate} findUserAccountImgByUserId(userId:73){imageId,userId,presentImgNum}}"
}
Error
{
"errors": [
{
"message": "Validation error of type SubSelectionRequired: Sub selection required for type Timestamp of field userAccountCreatedDate",
"locations": [
{
"line": 1,
"column": 103
}
]
}
]
}
Entity
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "view_user_account_point", schema = "public", catalog = "corus")
public class ViewUserAccountPoint {
#Id
#Basic
#GraphQLQuery(name = "rowNum")
#Column(name = "row_num", nullable = true)
private Long rowNum;
#Basic
#Column(name = "user_account_point_userid", nullable = true)
#GraphQLQuery(name = "userAccountPointUserId")
private Integer userAccountPointUserId;
#Basic
#Column(name = "subject_id", nullable = true)
#GraphQLQuery(name = "subjectId")
private Integer subjectId;
#Basic
#Column(name = "point", nullable = true)
#GraphQLQuery(name = "point")
private Integer point;
#Basic
#Column(name = "user_account_point_typeid", nullable = true)
#GraphQLQuery(name = "userAccountPointTypeId")
private Integer userAccountPointTypeId;
#Basic
#Column(name = "date_created", nullable = true)
#GraphQLQuery(name = "userAccountCreatedDate")
private Timestamp userAccountCreatedDate;
Service
public List<ViewUserAccountPoint> findUserPointByUserId(#GraphQLArgument(name = "userId") Integer userId){
return viewUserAccountPointRepository.findByUserAccountPointUserIdOrderByUserAccountCreatedDateDesc(userId);
}
Controller
private final GraphQL graphQL;
public UserController(UserAccountService userAccountService) {
GraphQLSchema schema = new GraphQLSchemaGenerator()
.withResolverBuilders(
//Resolve by annotations
new AnnotatedResolverBuilder())
.withOperationsFromSingleton(userAccountService,UserAccountService.class)
.withValueMapperFactory(new JacksonValueMapperFactory())
.generate();
graphQL = GraphQL.newGraphQL(schema).build();
}
#PostMapping(value = "/graphql", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
#ResponseBody
public Map<String, Object> graphql(#RequestBody Map<String, String> request, HttpServletRequest raw) {
ExecutionResult executionResult = graphQL.execute(ExecutionInput.newExecutionInput()
.query(request.get("query"))
.operationName(request.get("operationName"))
.context(raw)
.build());
return executionResult.toSpecification();
}
I search through all query timestamp format
However, i couldn't find
i hope to hear the solution.
thank you
For one reason or another, Timestamp got mapped incorrectly. It ended up being an object and not a scalar.
As mentioned in the issue you opened, it's unclear where is Timestamp in your code coming from.
java.sql.Timestamp is supported out of the box in recent versions of GraphQL SPQR, so you might be on an older version.
If that's not the case, it would mean Timestamp is some other than java.sql.Timestamp, and you'd need to register a custom mapper for it.
public class TimestampMapper implements TypeMapper {
// Define the scalar as needed, see io.leangen.graphql.util.Scalars for inspiration
private static final GraphQLScalarType TIMESTAMP = ...;
#Override
public GraphQLOutputType toGraphQLType(AnnotatedType javaType, OperationMapper operationMapper, Set<Class<? extends TypeMapper>> mappersToSkip, BuildContext buildContext) {
return TIMESTAMP; //it's important to always return the same instance
}
#Override
public GraphQLInputType toGraphQLInputType(AnnotatedType javaType, OperationMapper operationMapper, Set<Class<? extends TypeMapper>> mappersToSkip, BuildContext buildContext) {
return TIMESTAMP; //same as above
}
#Override
public boolean supports(AnnotatedType type) {
return ClassUtils.isSuperClass(Timestamp.class, type);
}
}
Then register your mapper:
generator.withTypeMappers(new TimestampMapper())
It's incorrect query body for my case, make sure you have the right one.
I am passing json object. All but Timestamp properties is being set correctly. Timestamp properties of Entity is set to 1970 year ...smth. How to fix it?
#RequestMapping(value = "/addProduct", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public Product addProduct(Model model, #RequestBody Product product, BindingResult result){
cService = (ProductService) context.getBean("productService");
cService.addProduct(product);
return product;
}
json input:
{"name": "ag","dateTimeStart": 1411568760,"dateTimeEnd": 1412100000}
Product entity:
#Table(name = "product")
#Entity
public class Product implements Serializable {
#Id
#Column(name = "product")
private int product;
#Column(name = "name")
#Basic
#NotEmpty
private String name;
#Column(name = "date_time_start")
#Basic
private Timestamp dateTimeStart;
#Column(name = "date_time_end")
#Basic
private Timestamp dateTimeEnd;
}
Added record to database:
name ag
dateTimeStart 1970-01-17 10:06:08
dateTimeEnd 1970-01-17 10:15:00
UPDATE
I am using MySQL. I pass unix epoch timestamp and expect it to be correctly stored in database.
You need to add a annotation for your JSON mapper, that instructs him to map the date via timestamp.
problem is rather simple, but kinda hard to explain.
I have REST service like this:
#Path("categories")
#RequestScoped
public class CategoryResource {
#Inject
CategoriesFacade dao;
#PUT
#Consumes("application/json")
public void putJson(Categories content) {
System.err.println(content.toString()); // <-- ?????
dao.edit(content);
}
// ....
}
And entity object like this:
#XmlRootElement
public class Categories implements Serializable {
#Id
#Column(name = "CATEGORY_ID", unique = true, nullable = false, precision = 5)
private Integer categoryId;
#Column(name = "NAME")
private String name;
#JoinColumn(name = "PARENT_ID", referencedColumnName = "CATEGORY_ID")
#ManyToOne
private Categories parentId;
// ....
}
Here is http request payload:
PUT http://localhost:8080/api/categories
Data: {"categoryId":"7162","name":"test","parentId":null}
And here is Categories object i get in #PUT request (see line marked with ????):
Categories{
categoryId=7162,
name=test,
parentId=Categories{categoryId=null, name=null, parentId=null}
}
So as you can see here, parentId is assigned with empty Categories object, this way
i'm getting ValidationException, because name could not be null.
Any idea how to make Jersey to give me this kind of object, so null value for parentId will be fine:
Categories{
categoryId=7162,
name=test,
parentId=null
}
Stringify your object on client site (for example, if client on JavaScropt, using method JSON.stringify).
Rest method make
#PUT
#Consumes("application/json")
public void putJson(String content) {
And convert json string to object (for eample, using google json)
Categories cont = new com.google.gson.Gson().fromJson(content, Categories.class);