Hibernate order by Id of child table - mysql

I have two tables as follows:
//TmCategory table
public class TmCategory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="CATEGORY_NAME")
private String categoryName;
#Column(name="OWNER_ID")
private BigInteger ownerId;
//bi-directional many-to-one association to TmCategoryRate
#OneToMany(mappedBy="tmCategory", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#javax.persistence.OrderBy("startDate ASC")
private Set<TmCategoryRate> tmCategoryRates;
//getters and setters
}
//TmCategoryRates Table
#Entity
#Table(name="tm_category_rates")
public class TmCategoryRate implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="CREATED_BY")
private BigInteger createdBy;
#Column(name="CREATED_DATE")
private Timestamp createdDate;
#Column(name="END_DATE")
private Timestamp endDate;
#Column(name="RATE")
private Double rate;
#Column(name="RATE_TYPE")
private String rateType;
#Column(name="START_DATE")
private Timestamp startDate;
//bi-directional many-to-one association to TmCategory
#ManyToOne
#JoinColumn(name="CATEGORY_ID")
private TmCategory tmCategory;
//getters and setters.....
}
And here is the detached criteria am using to get the categories
DetachedCriteria criteria = DetachedCriteria.forClass(TmCategory.class);
criteria.add(Restrictions.eq("id", catId));
DetachedCriteria criteria1 =criteria.createCriteria("tmCategoryRates");
criteria1.addOrder(Order.asc("id"));
List<TmCategory> categories = getHibernateTemplate().findByCriteria(criteria);
Here I am trying to sort the tmCategoryRates in ascending order of their ID.
I am able to get category if there is at least one tmCategoryRates is available for this category. If there is no tmCategoryRates available then its returning null instead of returning category with tmCategoryRates.

If there may be no rates for the category you need a left join. It is not clear why you are using DetachedCriteria here, but see this example from the Hibernate docs
List cats = session.createCriteria( Cat.class )
.createAlias("mate", "mt",
Criteria.LEFT_JOIN,Restrictions.like("mt.name", "good%") )
.addOrder(Order.asc("mt.age"))
.list();
Hibernate 3.5 -- 16.4 Associations

Related

Hibernate & MySQL: When a record is created, a second record is added and is completely null

I'm fairly new to spring and hibernate and I'm creating a simple application that has two entity classes that are linked by #OneToMany and #ManyToOne relationships.
#Entity
public class ActiveIngredient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#Column(name="active")
private String active;
#Column(name="potency")
private double potency;
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinColumn(name="manufacturer")
private Manufacturer manufacturer;
and
#Entity
public class Manufacturer {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int manId;
#Column(name="name")
private String manName;
#Column(name="country")
private String country;
#Column(name="city")
private String city;
#Column(name="email")
private String email;
#Column(name="phone")
private String phone;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH}, mappedBy = "manufacturer")
private List<ActiveIngredient> activeIngredients;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH}, mappedBy = "manufacturer")
private List<ExcipientIngredient>excipientIngredients;
On the front end, a thymeleaf template is used where users can submit the data to create an ingredient and manufacturer. Below is the controller:
#Controller
#RequestMapping("/excipients")
public class ExcipientIngredientController {
#Autowired
private ExcipientIngredientService excipientIngredientService;
#Autowired
private ManufacturerService manufacturerService;
#GetMapping("/listExc")
public String listExcipients(Model model) {
List<ExcipientIngredient> theExcipient = excipientIngredientService.findAll();
//add list to the model.
model.addAttribute("excipient", theExcipient);
//return the thymeleaf page with this path.
return "list-excipients";
}
#GetMapping("/addExcipientForm")
public String addForm(Model model){
//to add a new excipient ingredient, need to create a new object
ExcipientIngredient excipientIngredient = new ExcipientIngredient();
Manufacturer manufacturer = new Manufacturer();
//add to the model
model.addAttribute("excipientIngredient", excipientIngredient);
model.addAttribute("manufacturer", manufacturer);
return "add-excipient-form";
}
#PostMapping("/saveExc")
public String saveExcipients(#ModelAttribute("excipientIngredient") ExcipientIngredient excipientIngredient, #ModelAttribute("manufacturer") Manufacturer manufacturer) {
// save the Ingredient
excipientIngredientService.saveOrUpdate(excipientIngredient);
manufacturerService.saveOrUpdate(manufacturer);
// use a redirect to prevent duplicate submissions
return "redirect:/excipients/listExc";
}
}
and this is the implementation for the save/update method.
#Override
public void saveOrUpdate(ExcipientIngredient excipientIngredient) {
Session currentSession = entityManager.unwrap(Session.class);
currentSession.saveOrUpdate(excipientIngredient);
}
Everything works fine when creating an ingredient and being updated into MySQL database, however, when the manufacturer is added to the database, an extra record is created that is completely null:
MySQL entry
I've been trying for a few hours to resolve this issue but have had no luck. Any suggestions or pointing me in the right direction would be appreciated.
In the Manufacturer class, you can try removing the cascade parameter from the #OneToMany annotation.
Here's a good article that you might find useful: https://www.baeldung.com/hibernate-one-to-many

Foreign Keys in Spring Boot (MySQL, Hibernate, JPA)

I am trying to write a RESTful API using Spring Boot and I am not able to figure out a way to map my relations in the database. I have a User and a Reports table. Each User can have multiple Reports, and a single report consists of "FROM USER" and "TO USER" columns to indicate who sent the report and to whom. My User ID is the primary key and for the Report table, I am generating REPORT ID as the primary key using AUTO INCREMENT. Here is my User model class -
#Entity
#Table (name = "user")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
private List<Report> reportReceivedList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Report> reportSentList;
/* Getters and setters ..... */
}
Here is my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne()
#JoinColumn(name = "from_user_id")
private Long fromUserId; //THIS SHOULD BE FROM "USER" TABLE
#ManyToOne()
#JoinColumn(referencedColumnName = "to_user_id")
private Long toUserId; //THIS SHOULD BE FROM "USER" TABLE
#Temporal(TemporalType.DATE)
#CreatedDate
private Date createdAt;
private String observation;
private String context;
//Other variables and getters and setters .....
}
Can someone please show me a way to correctly define this relationship. My current model doesn't work. Also, I want rows from REPORT class to be deleted as soon as a user is deleted. Thanks!
I finally fixed it by changing my User class as follows -
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "to_user_id")
private List<Report> reportReceivedList;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "from_user_id")
private List<Report> reportSentList;
And by changing my Report class as -
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;

JPA Hibernate bidirectional relation ends in null pointer

I have 2 entities: Version and Change log.
A Version has N change Logs and a Change Log has one Version.
My Version entity looks like:
#Table(name="change_log_version")
#Entity
public class ChangeLogVersionEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="version")
private String version;
#Column(name="date")
private LocalDateTime date;
#OneToMany(mappedBy = "version", cascade=CascadeType.ALL, fetch =
FetchType.EAGER)
public List<ChangeLogEntity> changeLogEntities;
public void addChangeLog(ChangeLogEntity changeLogEntity) {
this.changeLogEntities.add(changeLogEntity);
changeLogEntity.setVersion(this);
}
...
}
My Change Log entity looks like:
#Table(name="change_log")
#Entity
public class ChangeLogEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name="description")
private String description;
#Column(name="added")
private boolean isAdded;
#Column(name="modified")
private boolean isModified;
#Column(name="deleted")
private boolean isDeleted;
#Column(name="public")
private boolean isPublic;
#Column(name="date")
private LocalDateTime date;
#ManyToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "version_id")
private ChangeLogVersionEntity version;
...
}
I am kinda new to hibernate and i am stuck in a null pointer by adding a change log to the version. What do i have to change to archive the relation?
Many thanks in advance so far :)
That's because the changeLogEntites List is not initialized.
You should initialize it in the declaration
#OneToMany(mappedBy = "version", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
public List<ChangeLogEntity> changeLogEntities = new ArrayList<>();

Spring Data JPA aggregation group by period of time

at the moment I develop a small eCommerce App with Spring Boot and AngularJS. For my data-access layer I use Spring Data JPA with a MySQL-DB. My next step is, that I want to plot some statistics for specific products. For example: how did the review-ratings from a specific product develop over time. So I would like to specify a period of time (e.g. 01.2016 - 03.2016) and than formulate one query that returns 10 or 15 Points in time (within the range) with the average rating of the reviews in this period. So that i can plot a Chart from that.
I found ways with Spring Data to get the average for one period (Between x and y), but then I would have to make 10 queries to the database (for each dot). So I want to know, if it is possible to formulate a query with spring data that splits a time-period in a fixed number of sub-periods and gets the average of customer-ratings within each sub-period? If yes, how can I achieve that?
An excerpt of my Data Model Looks as follows:
#Entity
#Table(name = "product_placement")
public class ProductPlacement implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_placement_id")
private long id;
#Column(name = "title")
private String title;
...
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="productPlacements")
private Set<CustomerReview> customerReviews;
}
#Entity
#Table(name = "customer_review")
public class CustomerReview implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "customer_review_id")
private String reviewIdentifier;
...
#Column(name = "rating")
private Integer rating;
#Column(name = "dateOfCreation", nullable=true, unique=false)
private LocalDateTime dateOfCreation;
#JsonBackReference
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "review_to_product",
joinColumns = #JoinColumn(name = "customer_review_id"),
inverseJoinColumns = #JoinColumn(name = "product_placement_id")
)
private Set<ProductPlacement> productPlacements;
}
Thank you!

How to return a List of objects in JSON when using JAX-RS

I have it working... kind of. Except when the Json is printed out, it prints out a lot of duplicate code. I suspect it has something to do with the way its marshalled. In any case, this is my code:
User.java
#XmlRootElement(name="user")
#Entity
#Table(name="users")
public class User implements Serializable{
private static final long serialVersionUID = 1L;
#XmlElement(required=true)
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#XmlElement(required=true)
#Column(name="username", nullable=false)
private String userName;
#XmlElement(required=true)
#Column(name="phoneid", nullable=false)
private String phoneid;
#XmlTransient
#ManyToMany(
cascade={CascadeType.ALL},
mappedBy="voters",
targetEntity=Vote.class,
fetch=FetchType.EAGER
)
private List<Vote> votes = new ArrayList<Vote>();
Vote.java
#XmlRootElement(name="vote")
#Entity
#Table(name="votes")
public class Vote implements Serializable {
private static final long serialVersionUID = 1L;
#XmlElement(required=true)
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id", nullable=false)
private int id;
#XmlElement(required=true)
#Column(name="name", nullable=false)
private String name;
#XmlElement(required=true)
#Column(name="max", nullable=false)
private int max;
#XmlElement(required=true)
#Column(name="current", nullable=false)
private int current;
#XmlElement(required=true)
#Column(name="is_simple", nullable=false)
private int isSimple;
#XmlTransient
#ManyToMany(cascade = {CascadeType.ALL}, targetEntity=User.class, fetch=FetchType.EAGER)
#JoinTable(name = "vote_user",
joinColumns = {#JoinColumn(name = "vote_id")},
inverseJoinColumns = {#JoinColumn(name = "user_id")}
)
private List<User> voters = new ArrayList<User>();
GetRestfulVote.java
#Path("/votes")
public class GetRestfulVote {
#EJB
VoteBeanInterface bean;
#GET
#Path("/{userid}")
#Produces(MediaType.APPLICATION_JSON)
public List<Vote> getVotesJson(#PathParam("userid") String userId) {
//Will be getting using the user id to be able to set a flag if the calling user voted in it
List<Vote> listOfVotes = bean.getVotes();
return listOfVotes;
}
}
Now as is fairly obvious from my code I have a many to many relationship between my Vote and User classes. I believe thats where the issue is occurring. But the problem is I don't know how to keep it from being omitted from the "jsonification" process. More importantly, how can I force it only to turn the owning class list (Votes) into Json. Any help and clarification would be greatly appreciated!