Why is my json returned from the controller with empty fields? - spring

I am using the debugger in IntelliJ and right before the point of returning the result, the array is perfectly fine, as you can see here
But for some reason, the response in the browser looks like this
I don't understand why the fields are invisible.
This is what my 2 models look like:
Municipality:
#Entity
public class Municipality {
#Id
#JsonIgnore
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
}
Prediction
#Entity
public class Prediction {
#Id
#JsonIgnore
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
LocalDateTime tsPredictionMade;
LocalDateTime tsPredictionFor;
float pm10;
float pm25;
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
Municipality municipality;
}
And this is my controller:
#RestController
#RequestMapping("/predict")
public class PredictionController {
private MunicipalityService municipalityService;
private PredictionService predictionService;
#Autowired
public PredictionController(MunicipalityService municipalityService, PredictionService predictionService) {
this.municipalityService = municipalityService;
this.predictionService = predictionService;
}
#GetMapping
public List<Municipality> getPredictions(){
List<Municipality> result = municipalityService.getPredictions();
return result;
}
#GetMapping("/{municipality}")
public List<Prediction> getPredictionsForMunicipality(#PathVariable("municipality") String name){
List<Prediction> result = predictionService.getPredictions(name);
return result;
}
}
The rest of the app (service and persistence layer) is pretty standard.
What is the reason for this?

You will need the getters and setters for your models. The Jackson library needs it for accessing its fields when converting the models into JSON, differently from JPA when converting the resultSet into models. Here is the code:
Prediction
#Entity
public class Municipality {
#Id
#JsonIgnore
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public LocalDateTime getTsPredictionMade() {
return tsPredictionMade;
}
public void setTsPredictionMade(LocalDateTime tsPredictionMade) {
this.tsPredictionMade = tsPredictionMade;
}
public LocalDateTime getTsPredictionFor() {
return tsPredictionFor;
}
public void setTsPredictionFor(LocalDateTime tsPredictionFor) {
this.tsPredictionFor = tsPredictionFor;
}
public float getPm10() {
return pm10;
}
public void setPm10(float pm10) {
this.pm10 = pm10;
}
public float getPm25() {
return pm25;
}
public void setPm25(float pm25) {
this.pm25 = pm25;
}
public Municipality getMunicipality() {
return municipality;
}
public void setMunicipality(Municipality municipality) {
this.municipality = municipality;
}
}
Municipality
#Entity
public class Municipality {
#Id
#JsonIgnore
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

You need getters and setter for each field that you want to expose.
You can use #Data from lombok project to avoid boilerplate code.
https://projectlombok.org/

Related

I have a problem with "You may also like" feature in Spring boot JPA

I want to create a simple "You may also like" feature for a blog.
There are posts and each of them has one or more tags. Also a tag can contain many posts. I want to implement the feature where you open a post and the posts, which have similar tags, are recommended to you.
So i created 3 entities:
Post.java
#Entity
#Table
public class Post {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String text;
#Column
private String author;
#OneToMany(mappedBy = "post")
Set<PostTags> postTags;
public Post(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Set<PostTags> getPostTags() {
return postTags;
}
public void setPostTags(Set<PostTags> postTags) {
this.postTags = postTags;
}
}
Tags.java
#Entity
#Table
public class Tags {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column
private String name;
#OneToMany(mappedBy = "tag")
Set<PostTags> postTags;
public Set<PostTags> getPostTags() {
return postTags;
}
public void setPostTags(Set<PostTags> postTags) {
this.postTags = postTags;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
PostTags.java
#Entity
public class PostTags {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column
private Long id;
#ManyToOne
#JoinColumn(name = "post_id")
private Post post;
#ManyToOne
#JoinColumn(name = "tag_id")
private Tags tag;
public PostTags(){}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public Tags getTag() {
return tag;
}
public void setTag(Tags tag) {
this.tag = tag;
}
}
And repositories:
#Repository
public interface PostTagsRepository extends JpaRepository<PostTags, Long> {
#Query("select p.post from PostTags p where p.tag.id IN :tagIds")
Set<Post> findPostsbyTagIds (List<Long> tagIds);
}
#Repository
public interface PostRepository extends JpaRepository<Post, Long> {
#Query("select p from Post p where p.author = :author")
Set<Post> findPostsByAuthor(String author);
}
I managed to create this feature in an amateurish way, but better than nothing. I piled up everything in one method just to test it:
#GetMapping("/posts")
public Set<Post> showRecommendedPosts(){
//Imitate post id
long postId = 1;
Post postFound = postRepository.findById(postId).get();
Set<PostTags> postTags = postFound.getPostTags();
List<Long> listTagIds = new ArrayList<>();
//extract ids of the tags from the post
for(PostTags tag : postTags){
listTagIds.add(tag.getTag().getId());
}
//find posts by Author
Set<Post> postsByAuthor = postRepository.findPostsByAuthor(postFound.getAuthor());
//find posts by Tags
Set<Post> postsByTagIds = postTagsRepository.findPostsbyTagIds(listTagIds);
//We combine both sets
Set<Post> recommendedPosts = new HashSet<>(postsByAuthor);
recommendedPosts.addAll(postsByTagIds);
recommendedPosts.remove(postFound);
return recommendedPosts;
}
But this works only if i manually add data to "post_tags" table in the db like this:
Here is my question, i don't know how to add multiple tags to a post in Spring. Because it would be something like this:
PostTags newPostTag1 = new PostTags();
newPostTag.setPost(post1);
newPostTag.setTag(tag1);
PostTags newPostTag2 = new PostTags();
newPostTag2.setPost(post1);
newPostTag2.setTag(tag2);
PostTags newPostTag3 = new PostTags();
newPostTag3.setPost(post1);
newPostTag3.setTag(tag3);
And so on...
Therefore, it's not an option. So how can i save tags correctly? Or have my entities been created incorrectly? What is my mistake? Thank you!
I am not sure to understand the idea behind Tag being an Entity.
How I see it is you use the postTags and change it to tags. This tags would be a Set of an enum if you want to restrict the user or a Set of String other way. After that, I would add an endpoint that return post based on a tag or a list of tags for your You may also like feature. This endpoint just make a request to the database (find posts where tags contains givenTag max 10). At the end, you only have one entity :
#Entity
#Table
public class Post {
#Id
#Column
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String text;
#Column
private String author;
#Column
#Convert(converter = StringListConverter.class)
Set<String> tags;
// ...
}
Converter implementation here

Inconsistent datatypes: expected TIMESTAMP got NUMBER

I am using Hibernate 5.3.10 as my ORM in the Spring boot based project. Suppose that we have the following entities:
#Entity
#Table(name = "parent")
#Inheritance(strategy = InheritanceType.JOINED)
public class Parent {
#EmbeddedId
private EmbId id;
public Id getId() { return id; }
public void setId(Id id) { this.id = id; }
}
#Entity
#Table(name = "child")
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "id"),
#PrimaryKeyJoinColumn(name = "date_time")
})
public class Child {
#EmbeddedId
private EmbId id;
private String name;
public Id getId() { return this.id; }
public void setId(Id id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
and the embeddable key as follow:
#Embeddable
public class EmbId {
#Column(name = "id")
private Long id;
#Column(name = "date_time")
private Date dateTime;
public Long getId() { return this.id; }
public void setId(Long id) { this.id = id; }
public Date getDateTime() { return this.dateTime; }
public void setDateTime(Date dateTime) { this.dateTime = dateTime; }
}
When I want to polymorphic query on Child entity, the JPA faces with ORA-00932 Inconsistent datatypes: expected TIMESTAMP got NUMBER and the following sql have seen in the console:
select child0_.id as date_time1_25_0, child0_.date_time as id2_25_0, child0_1_.name as name3_3_0 from my_schema.child child0_ inner join my_schema.parent child0_1_ on child0_.id = child0_1_.date_time and child0_.date_time = child0_1_.id
It seems that the equality of IDs is displaced. What happened and what should I do to resolve that?
Thanks in advance.
Fortunately, I found that adding referencedColumnName attribute to #PrimaryKeyJoinColumn could be guide hibernate to use the IDs in the right place.
So, the Child class should be as follow:
#Entity
#Table(name = "child")
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "id"),
#PrimaryKeyJoinColumn(name = "date_time")
})
public class Child {
#EmbeddedId
private EmbId id;
private String name;
public Id getId() { return this.id; }
public void setId(Id id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}

null values inserted while auditing

My AuditListener
public class EmployeeAuditListeners {
#PrePersist
public void prePersist(Employee employee){
perform(employee,Action.INSERTED);
}
#PreUpdate
public void preUpdate(Employee employee){
perform(employee,Action.UPDATED);
}
#PreRemove
public void preRemove(Employee employee){
perform(employee,Action.DELETED);
}
#Transactional
public void perform(Employee emp, Action action){
EntityManager em = BeanUtil.getBean(EntityManager.class);
CommonLogs commonLogs = new CommonLogs();
commonLogs.setQuery("new query");
em.persist(commonLogs);
}
}
and My Auditable.class
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
#CreatedBy
protected U createdBy;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date createdDate;
#LastModifiedBy
protected U lastModifiedBy;
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date lastModifiedDate;
}
My CommonLogs.class
#Entity
#EntityListeners(AuditingEntityListener.class)
public class CommonLogs extends Auditable<String> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String query;
public CommonLogs() {
}
public CommonLogs(String query) {
this.query = query;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
My Employee.java class
#Entity
#EntityListeners(EmployeeAuditListeners.class)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
and I have a simple Rest Controller
#RestController
#RequestMapping("/api")
public class EmployeeController {
#Autowired
private EmployeeRepository employeeRepository;
#PostMapping("/employees")
public Employee createEmployee(#RequestBody Employee employee){
return employeeRepository.save(employee);
}
}
I want to log it on my table (common_logs) every time i perform some crud operations on my Employee Entity.
the above given example is working to some extent as it successfully stores employee and invokes EmployeeAuditListeners.
but now while saving CommongLog entity i expect it's parent class Auditable to automatically insert createdBy, createdDate etc. for now only query and id is inserted on common_logs table and remaining columns are null.
You can review the documentation for Auditing in here.
To enable the automatic Auditing, you must add the annotation #EnableJpaAuditing in your Application class:
#SpringBootApplication
#EnableJpaAuditing
class Application {
static void main(String[] args) {
SpringApplication.run(Application.class, args)
}
}
If you want the fields #CreatedBy and #LastModifiedBy too, you will also need to implement the AuditorAware<T> interface. For example:
class SpringSecurityAuditorAware implements AuditorAware<User> {
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return ((MyUserDetails) authentication.getPrincipal()).getUser();
}
}

How to combine two DAOs in generic way with only one method in Service layer in Spring Boot

Is it possible to combine two DAOs into one Service method?
I want to create a generic method which will choose correct DAO based on the input parameter. What for now I came up with is the method which will accept Dao from the outside the service object. But this requires to initialize appropriate Dao in the Controller which is a little bit ugly...
Measurement is just an interface for Temperature.java and Humidity.java entities with separate tables on PostgreSQL.
#Service
public class MeasurementService {
#Autowired
private TemperatureDao temperatureDao;
#Autowired
private HumidityDao humidityDao;
public<T extends PagingAndSortingRepository<Measurement, Long>> void insertMeasurementForUser(String username, List<Measurement> measurements, T dao) {
dao.saveAll(measurements);
}
}
TemperatureDao.java
#Repository
public interface TemperatureDao extends PagingAndSortingRepository<Temperature, Long> {
#Query("select u from Temperature u where u.owner = ?1 order by u.id desc")
List<Temperature> findLatestTemperatureForUser(User user, Pageable pageable);
}
HumidityDao.java
#Repository
public interface HumidityDao extends PagingAndSortingRepository<Humidity, Long> {
#Query("select u from Humidity u where u.owner = ?1 order by u.id desc")
List<Humidity> findLatestHumidityForUser(User user, Pageable pageable);
}
Temperature.java
#Entity
#Table(name = "temperature")
public class Temperature implements Measurement {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "th1value")
private Float th1Value;
#Column(name = "timestamp")
#NotNull
private LocalDateTime timestamp;
#ManyToOne
#JoinColumn(name = "user_id")
#NotNull
private User owner;
public Temperature() {
}
public Temperature(Float th1Value, LocalDateTime timestamp, User owner) {
this.th1Value = th1Value;
this.timestamp = timestamp;
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
public LocalDateTime getTimestamp() {
return timestamp;
}
#JsonSerialize(using = LocalDateTimeSerializer.class)
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
#Override
public User getOwner() {
return owner;
}
#Override
public void setOwner(User owner) {
this.owner = owner;
}
}
Humidity.java
#Entity
#Table(name = "humidity")
public class Humidity {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "hum1value")
private Float hum1Value;
#Column(name = "timestamp")
#NotNull
private LocalDateTime timestamp;
#ManyToOne
#JoinColumn(name = "user_id")
#NotNull
private User owner;
public Humidity() {
}
public Humidity(Float hum1Value, LocalDateTime timestamp, User owner) {
this.hum1Value = hum1Value;
this.timestamp = timestamp;
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
public LocalDateTime getTimestamp() {
return timestamp;
}
#JsonSerialize(using = LocalDateTimeSerializer.class)
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner = owner;
}
}
Any ideas?
You could write a Resolver pattern to return needed dao based on your conditions. You service will use the resolver to get the correct dao.
public HellDao implements BaseDao {
public void save();
}
public ByeDao implements BaseDao {
public void save();
}
public DaoResolver {
#Autowired
private helloDao;
#Autowired
private byeDao;
public BaseDao resolve(Object input) {
//based on input return the correct dao
BaseDao resolvedDao = null;
switch(input.enum) {
case Hello:
resolvedDao = helloDao;
break;
case Hello:
resolvedDao = byeDao;
break;
default:
//decide something for default
}
return resolvedDao;
}
}
public class MyService {
#Autowired
private DaoResolver daoResolver;
public Object doSomething() {
BaseDao dao = daoResolver.resolve(someObject);
//you will get HelloDao or ByeDao based on the input
dao.save();
}
}
You can check for the type of measurements using instanceof so you could do it without generics.
public void insertMeasurementForUser(String username, List<Measurement> measurements) {
if(measurements.get(0) instanceof Temperature)
temperatureDao.saveAll(measurements);
else if(measurements.get(0) instanceof Humidity)
humidityDao.saveAll(measurements);
}

how to Fix spring boot one to many bidirectional infinity loop?

i am try to create a one to many bidirectional mapping using spring boot and spring data jpa please look the below entity
Employer Entity
#Entity
public class Employer
{
private Long id;
private String employerName;
private List<Employee> employees;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getEmployerName()
{
return employerName;
}
public void setEmployerName(String employerName)
{
this.employerName = employerName;
}
#OneToMany(cascade=CascadeType.ALL, mappedBy="employer")
public List<Employee> getEmployees()
{
return employees;
}
public void setEmployees(List<Employee> employees)
{
this.employees = employees;
}
}
Employee Entity
#Entity
public class Employee
{
private Long id;
private String employeeName;
private Employer employer;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getEmployeeName()
{
return employeeName;
}
public void setEmployeeName(String employeeName)
{
this.employeeName = employeeName;
}
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
public Employer getEmployer()
{
return employer;
}
public void setEmployer(Employer employer)
{
this.employer = employer;
}
}
Employer Repo
public interface EmployerServices extends JpaRepository<Employer, Long> {
}
Employee Repo
public interface EmployeeServices extends JpaRepository<Employee, Long> {
}
REST Controller is
#RestController
public class Controller {
#Autowired EmployeeServices employeeServices;
#Autowired EmployerServices employerServices;
#GetMapping("/getempr")
public Object getempr(){
return employerServices.findOne(1L);
}
}
now the problem begin start see my out put
its look like a infighting loop and my server throwing error getOutputStream() has already been called for this response.
I used #JsonBackReference & #JsonManagedReference
annotation but the problem is its working like one to many
{
"id":1,
"employerName":"employer",
"employees":[
{"id":1,"employeeName":"emp1"},
{"id":2,"employeeName":"emp2"}
]
}
if I am trying to get in the concern of many to one like all employee with employer. the output is
[
{
"id":1,
"employeeName":"emp1"
},
{
"id":2,
"employeeName":"emp2"}
]
its not showing me the employer details.
please suggets me guys what i am doing wrong. thanks in advance!!
Instead of using #JsonBackReferenceand #JsonManagedReference try to use annotation #JsonIgnoreProperties:
#JsonIgnoreProperties("employer")
private List<Employee> employees;
#JsonIgnoreProperties("employees")
private Employer employer;
It prevents Jackson from rendering a specified properties of associated objects.
with the JSON its a problem with bi-directional mapping. Use the below properties.
#JsonIgnoreProperties("employer")
#JsonIgnoreProperties("employees")
please keep fetching type as eager.
hope this will work.
You can solve your issue with two modification with annotations.
Employer.class
#Entity
public class Employer {
private Long id;
private String employerName;
#OneToMany(cascade = CascadeType.ALL,
mappedBy = "employer",
orphanRemoval = true)
private List<Employee> employees;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployerName() {
return employerName;
}
public void setEmployerName(String employerName) {
this.employerName = employerName;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
Employee.class
#Entity
public class Employee {
private Long id;
private String employeeName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "employer_id")
private Employer employer;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Employer getEmployer() {
return employer;
}
public void setEmployer(Employer employer) {
this.employer = employer;
}
}
For more information please visit this link.
Change your getEmployer Method like this:
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
public Employer getEmployer()
{
return employer;
}
use
#JsonProperty(access = Access.WRITE_ONLY)
private List<Employee> employees;
So that it will ignore employees while printing to JSON in the response (and thus prevents the looping), but will still consider the JSON data (employee list) you pass in the request body so that it is available for persistence.

Resources