I'm a student, new to Stack Overflow, spring-boot and hibernate stack.
The problem: return a list of questions based on their category id.
As the in my model entity classes below I have no attributes for categoryId(foreign key) as this is being generated automatically through the relationship annotations.
I seem to be able to return a list of all questions with no level of filtering, but would also like a method that returns the questions based on their category id(foreign key).
Any help would be greatly appreciated, Thanks!
Using hibernate 5.2
categoryModel
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 3, max = 20)
private String name;
#NotNull
#Pattern(regexp = "#[0-9a-fA-F]{6}")
private String colorCode;
#OneToMany(mappedBy = "category")
private List<Question> questions = new ArrayList<>();
public Category(){}
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 List<Question> getQuestions() {
return questions;
}
public String getColorCode() {
return colorCode;
}
public void setColorCode(String colorCode) {
this.colorCode = colorCode;
}
questionModel
#Entity
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#ManyToOne
private Category category;
private LocalDateTime dateCreated = LocalDateTime.now();
public Question(){}
public String getTimeSinceUploaded() {
String unit = "";
LocalDateTime now = LocalDateTime.now();
long diff;
if((diff = ChronoUnit.SECONDS.between(dateCreated,now)) < 60){
unit = "secs";
} else if ((diff = ChronoUnit.MINUTES.between(dateCreated,now)) < 60) {
unit = "mins";
} else if ((diff = ChronoUnit.HOURS.between(dateCreated,now)) < 24) {
unit = "hours";
} else if ((diff = ChronoUnit.DAYS.between(dateCreated,now)) < 30) {
unit = "days";
} else if ((diff = ChronoUnit.MONTHS.between(dateCreated,now)) < 12) {
unit = "months";
} else{
diff = ChronoUnit.YEARS.between(dateCreated,now);
}
return String.format("%d %s",diff,unit);
}
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 Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
public LocalDateTime getDateCreated() {
return dateCreated;
}
public void setDateCreated(LocalDateTime dateCreated) {
this.dateCreated = dateCreated;
}
}
question DAO
#Repository
public class QuestionDaoImpl implements IQuestionDao {
#Autowired
private SessionFactory sessionFactory;
/**
* Returns a list of all the questions - michael
*/
#Override
public List<Question> findAll() {
Session session = sessionFactory.openSession();
// Create CriteriaBuilder
CriteriaBuilder builder = session.getCriteriaBuilder();
// Create CriteriaQuery
CriteriaQuery<Question> criteria = builder.createQuery(Question.class);
// Specify criteria root
criteria.from(Question.class);
// Execute query
List<Question> questions = session.createQuery(criteria).getResultList();
session.close();
return questions;
}
/**
* Returns A question by using the id - michael
*/
#Override
public Question findById(Long id) {
Session session = sessionFactory.openSession();
Question question = session.get(Question.class, id);
session.close();
return question;
}
/**
* TODO: ml- Finds a list of questions according to the category_id
*/
#Override
public List<Question> findByCategoryId(Long categoryId){
// This is a issue i a have here
return null;
}
try this, you have to pass category object bcz you created object in question entity,
#Override
public List<Question> findByCategoryId(Long categoryId){
Session session = sessionFactory.openSession();
final Criteria cr = session.createCriteria(Question.class);
cr.add(Restrictions.eq("category", category));
return cr.list();
}
Let me know if any quires.
Is your Dao extends crudrepository or JpaRepository?
Related
I am beginner to dealing with database.Currently, I am using a PostgreSQL database.
This is post request model where I am getting a list of question.
public class QuestionListRequest {
List<QuestionRequest> questionList;
public List<QuestionRequest> getQuestionList() {
return questionList;
}
public void setQuestionList(List<QuestionRequest> questionList) {
this.questionList = questionList;
}
This is my Question table where I have to store the Question getting about post request.
#Entity
#Table(name = "ns_question_b")
public class Question extends DomainObject {
#GeneratedValue(strategy = GenerationType.TABLE, generator = "question_gen")
#TableGenerator(name = "question_gen", table = "question_id_gen", pkColumnName = "GEN_NAME", valueColumnName = "GEN_VAL", pkColumnValue = "QuestionId_Gen", initialValue = 100000, allocationSize = 1000)
#Id
#Column(name = "question_id")
private BigInteger questionId;
#Column(name = "question_text")
private String questionText;
#Column(name = "question_type")
#Enumerated(EnumType.STRING)
private QuestionType questionType;
#Column(name = "question_timeout")
private Double questionTimeout;
#Column(name = "marks")
private Double marks;
#Column(name = "negative_mark")
private Double negativeMark;
#Column(name = "question_position")
private Double questionPosition;
#Column(name = "question_options")
private String questionOptions;
#Column(name = "subject")
private String subject;
public Question() {
super();
}
public Question(BigInteger questionId, String questionText, QuestionType questionType, Double questionTimeout, Double marks, Double negativeMark, Double questionPosition, String questionOptions, String subject, Answer answer) {
super();
this.questionId = questionId;
this.questionText = questionText;
this.questionType = questionType;
this.questionTimeout = questionTimeout;
this.marks = marks;
this.negativeMark = negativeMark;
this.questionPosition = questionPosition;
this.questionOptions = questionOptions;
this.subject = subject;
}
public Question(String createdBy, Timestamp creationDate, int version, Timestamp lastModifiedDate,
String lastModifiedBy, RecordStatus recordStatus) {
super(createdBy, creationDate, version, lastModifiedDate, lastModifiedBy, recordStatus);
}
public BigInteger getQuestionId() {
return questionId;
}
public void setQuestionId(BigInteger questionId) {
this.questionId = questionId;
}
public String getQuestionText() {
return questionText;
}
public void setQuestionText(String questionText) {
this.questionText = questionText;
}
public QuestionType getQuestionType() {
return questionType;
}
public void setQuestionType(QuestionType questionType) {
this.questionType = questionType;
}
public Double getQuestionTimeout() {
return questionTimeout;
}
public void setQuestionTimeout(Double questionTimeout) {
this.questionTimeout = questionTimeout;
}
public Double getMarks() {
return marks;
}
public void setMarks(Double marks) {
this.marks = marks;
}
public Double getNegativeMark() {
return negativeMark;
}
public void setNegativeMark(Double negativeMark) {
this.negativeMark = negativeMark;
}
public Double getQuestionPosition() {
return questionPosition;
}
public void setQuestionPosition(Double questionPosition) {
this.questionPosition = questionPosition;
}
public String getQuestionOptions() {
return questionOptions;
}
public void setQuestionOptions(String questionOptions) {
this.questionOptions = questionOptions;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
Now my question is How to store all the question one by one in the question table on a single calling with database?
I find many solution but there is only row to row mapping and I don't want this. I want all the question will directly stored in a Question table in a single calling.
Not clear about your question, the heading says about One to Many issue but explanation is not clear.
If you require to save the objects in a batch, you can add all questions to a list, add it to session and flush it at a certain limit.
Need to set this property,
hibernate.jdbc.batch_size 20
And implement following code,
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
if ( i % 20 == 0 ) { //20, same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
This is taken from http://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch15.html
These are my model classes:
Film.java
#Entity
public class Film {
private Integer id;
private String title;
private List<FilmActor> filmActors;
#OneToMany(mappedBy = "film")
public List<FilmActor> getFilmActors() {
return filmActors;
}
public void setFilmActors(List<FilmActor> filmActors) {
this.filmActors = filmActors;
}
}
Actor.java
#Entity
public class Actor {
private Integer id;
private String firstname;
private String lastname;
private List<FilmActor> filmActors;
#OneToMany(mappedBy = "actor")
public List<FilmActor> getFilmActors() {
return filmActors;
}
public void setFilmActors(List<FilmActor> filmActors) {
this.filmActors = filmActors;
}
}
And this is the Join Table Entity:
#Entity
#Table(name = "film_actor")
public class FilmActor {
private FilmActorPK id;
private Film film;
private Actor actor;
private Timestamp lastUpdate;
#EmbeddedId
public FilmActorPK getId() {
return id;
}
public void setId(FilmActorPK id) {
this.id = id;
}
#ManyToOne
#MapsId("film")
#JoinColumn(name = "film_id")
public Film getFilm() {
return film;
}
public void setFilm(Film film) {
this.film = film;
}
#ManyToOne
#MapsId("actor")
#JoinColumn(name = "actor_id")
public Actor getActor() {
return actor;
}
public void setActor(Actor actor) {
this.actor = actor;
}
#Column(name = "last_update")
public Timestamp getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(Timestamp lastUpdate) {
this.lastUpdate = lastUpdate;
}
}
and the Primary Key class:
#Embeddable
public class FilmActorPK implements Serializable {
private int actorId;
private int filmId;
#Column(name = "actor_id")
public int getActorId() {
return actorId;
}
public void setActorId(int actorId) {
this.actorId = actorId;
}
#Column(name = "film_id")
public int getFilmId() {
return filmId;
}
public void setFilmId(int filmId) {
this.filmId = filmId;
}
}
So I want to find films where 2 given actors acts. This is what I have:
#Override
public Collection<Film> filmsActorsTogether(Actor a, Actor b) {
final List<Film> filmsOfActorA = filmsOfActor(a);
final List<Film> filmsOfActorB = filmsOfActor(b);
final Collection<Film> intersection = CollectionUtils.intersection(filmsOfActorA, filmsOfActorB);
return intersection;
}
#Override
public List<Film> filmsOfActor(Actor actor) {
final EntityManager entityManager = persistenceUtil.getEntityManager();
final Actor persistentActor = entityManager.find(Actor.class, actor.getId());
final ArrayList<Film> films = new ArrayList<Film>();
for (FilmActor filmActor : persistentActor.getFilmActors()) {
films.add(filmActor.getFilm());
}
entityManager.close();
return films;
}
Is there any way to achieve this without fetching ALL films of 2 actors, and using filtering in memory? How do I get the Films directly from the DB with JQL?
Maybe there is something more elegant, but the following query should work:
select f from Film f where
(select count(fa.id) from FilmActor fa
where fa.film = f
and (fa.actor = :actor1 or fa.actor = :actor2)) = 2
Side note: your PK class should have a correct equals() and hashCode() methods
public User updateUser(User user) {
try {
User result = session.get(User.class, user.getId());
if (result == null) {
throw new FilamentNoSuchRecordException(new CoreError(304, "User does not exist"));
}
session.clear();
session.update(user);
return user;
} catch (HibernateException e) {
e.printStackTrace();
}
throw new FilamentDataConnectivityException(new CoreError(305,"Connectivity issue. Please see System Administrator"));
}
customer model is as follows
#Entity
#Table(name = "customers")
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#DynamicUpdate(value=true)
#SelectBeforeUpdate(value=true)
#SQLDelete(sql="Update customers SET deleted = true where customer_id=?")
#Where(clause="deleted != true")
#ApiModel(description="Create or update Customers")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Customer {
#Id
#Column(name="customer_id")
#NotNull
#GeneratedValue(strategy=GenerationType.AUTO)
private int id = 0;
#Column(name="name")
#ApiModelProperty(value="The name of the customer", example="Mr J. Bond")
#NotNull
private String name;
#Column(name="description")
#ApiModelProperty(value="Desciption of the customer")
#NotNull
private String description;
#Column(name="logo_url")
#ApiModelProperty(value="Logo of user")
#NotNull
private String logo;
#Column(name="created_at")
#ApiModelProperty(value="The date the item was created", example="")
#NotNull
private Date createdAt;
#Column(name="updated_at")
#ApiModelProperty(value="The date the item was updated", example="")
#NotNull
private Date updatedAt;
#ApiModelProperty(hidden=true)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private Set<Application> applications = new HashSet<Application>();
#ManyToMany(mappedBy = "customers")
private Set<Service> services = new HashSet<Service>();
#ApiModelProperty(hidden=true)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private Set<User> users = new HashSet<User>();
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "customer_subscription",
joinColumns = #JoinColumn(name = "customer_id"),
inverseJoinColumns = #JoinColumn(name = "subscription_id")
)
private Set<Subscription> subscriptions = new HashSet<Subscription>();
#ApiModelProperty(hidden=true)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "customer")
private Set<Corpus> corpus = new HashSet<Corpus>();
#Column(name="deleted")
#NotNull
private boolean deleteFlag;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Application> getApplications() {
return applications;
}
public void setApplications(Set<Application> applications) {
this.applications = applications;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public Set<Service> getServices() {
return services;
}
public void setServices(Set<Service> services) {
this.services = services;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
public Set<Corpus> getCorpus() {
return corpus;
}
public void setCorpus(Set<Corpus> corpus) {
this.corpus = corpus;
}
public Set<Subscription> getSubscriptions() {
return subscriptions;
}
public void setSubscriptions(Set<Subscription> subscriptions) {
this.subscriptions = subscriptions;
}
public boolean getDeleteFlag() {
return deleteFlag;
}
public void setDeleteFlag(boolean deleteFlag) {
this.deleteFlag = deleteFlag;
}
}
I check whether the object exists within the database, then update with an object, for example all fields could be null apart from the ID and the one thats needs to be updated. All fields are set to #NotNull in the model and I am using the #DynamicUpdate(value=true) and #SelectBeforeUpdate(value=true) annotations, but these seem to do nothing.
Just get failure saying the null fields can not be null. How do I update the row?
As we discussed in above comments, try this -
public User updateUser(User user) {
try {
User result = session.get(User.class, user.getId());
if (result == null) {
throw new FilamentNoSuchRecordException(new CoreError(304, "User does not exist"));
}
result.setName(user.getName()); // update some properties
session.update(result); // you should update 'result', not 'user'
return result;
} catch (HibernateException e) {
e.printStackTrace();
throw new FilamentDataConnectivityException(new CoreError(305,"Connectivity issue. Please see System Administrator"));
}
}
By using this method I found in another stack overflow post solved the issue. This checks each field and uses the 'not null' value. Then i can update from an object with only 1 field changed.
public static <T> T getNotNull(T a, T b) {
return b != null && a != null && !a.equals(b) ? a : b;
}
I have fetched a list of objects of type Company from the database. But when trying to serialize the list using Jackson to JSON, throws an error
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.zerosolutions.repository.entity.CompanySector.companies, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.zerosolutions.repository.entity.Company["sector"]->com.zerosolutions.repository.entity.CompanySector["companies"])
Company:
#Entity
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "name")
private String name;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "sector")
private CompanySector sector;
#OneToOne
#JoinColumn(name = "id")
private CompanyProfile profile;
#OneToOne
#JoinColumn(name = "id")
private CompanyAddress address;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
private List<Job> jobs = new ArrayList<>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CompanySector getSector() {
return sector;
}
public void setSector(CompanySector sector) {
this.sector = sector;
}
public CompanyProfile getProfile() {
return profile;
}
public void setProfile(CompanyProfile profile) {
this.profile = profile;
}
public CompanyAddress getAddress() {
return address;
}
public void setAddress(CompanyAddress address) {
this.address = address;
}
public List<Job> getJobs() {
return jobs;
}
public void setJobs(List<Job> jobs) {
this.jobs = jobs;
}
}
CompanySector:
#Entity
#Table(name = "sectors")
public class CompanySector {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name="sector")
private String sector;
#OneToMany(mappedBy="sector", cascade=CascadeType.PERSIST)
private List<Company> companies = new ArrayList<>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getSector() {
return sector;
}
public void setSector(String sector) {
this.sector = sector;
}
public List<Company> getCompanies() {
return companies;
}
}
Conversion code:
#RequestMapping(value = "/getCompanyList", method = RequestMethod.GET, produces={ "application/json"})
public #ResponseBody String getCompanyList() {
ObjectMapper mapper = new ObjectMapper();
try {
List<Company> companyList = companyServices.getCompanyList();
String companyListString = mapper.writeValueAsString(companyList); // this line throws error
return companyListString;
} catch (JsonProcessingException e) {
logger.error(e);
return null;
}
}
Any Suggestions what might be wrong here ?
Fetching list:
public List<Company> getCompanyList(){
Session session = sessionFactory.openSession();
Transaction tx = null;
try{
tx = session.beginTransaction();
List<Company> companies = session.createCriteria(Company.class).list();
logger.debug(companies);
tx.commit();
System.out.println("companies fetched");
return companies;
} catch(Exception e){
logger.error("Exceptino thrown: " + e);
tx.rollback();
return null;
} finally{
session.close();
}
}
If you know that you'll want to see all Companies every time you retrieve a CompanySector then change your field mapping for Companies to:
#OneToMany(fetch = FetchType.EAGER, mappedBy="sector", cascade=CascadeType.PERSIST)
private List<Company> companies = new ArrayList<>();
Another approach use Hibernate.initialize :
Hibernate.initialize(companySector.getCompany());
Also see this link it's very helpful
I am quite interested in a Hibernate mapping such as the Order/Product/LineItem described here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/example-mappings.html#example-mappings-customerorderproduct
The documentation seems quite thorough, but I am a bit unclear on the semantics of the Java classes that one would create...
Any hints much appreciated.
Thank you!
Misha
Using that example, you would have classes that looked like the following:
import java.util.HashSet;
import java.util.Set;
public class Customer {
private String name = null;
private Set<Order> orders = new HashSet<Order>();
private long id = 0;
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 Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
}
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Order {
private long id = 0;
private Date date = null;
private Customer customer = null;
private List<LineItem> lineItems = new ArrayList<LineItem>();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public List<LineItem> getLineItems() {
return lineItems;
}
public void setLineItems(List<LineItem> lineItems) {
this.lineItems = lineItems;
}
}
public class LineItem {
private int quantity = 0;
private Product product = null;
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
public class Product {
private long id = 0;
private String serialNumber = null;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
}
If you create the tables as per the structure in the example, that should set you up.
They show the UML and the class members in the diagram.
A Customer can have zero to many Order objects (Set).
An Order has at least one to many LineItem objects. {List).
A LineItem entry corresponds to a Product. So LineItem has exactly one Product object.
Not sure what your question is?