I have a class Comment:
#Entity
#Table(name = Constants.COMMENTS_TABLE)
#Audited
public class Comment {
#Column(name = "comment", nullable = false)
private String comment;
#ElementCollection(targetClass = CommentTopic.class)
#Enumerated(EnumType.STRING)
#Fetch(value = FetchMode.JOIN)
#CollectionTable(name = Constants.COMMENTS_TOPIC_JOIN_TABLE, joinColumns = #JoinColumn(name = "comment_id"))
#Column(name = "topic")
private Set<CommentTopic> commentTopics;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "comment_id", nullable = false)
private Long commentId;
}
Persisting the comment class works but the following criteria query:
Criteria criteria = session.createCriteria(Comment.class)
.add(Restrictions.eq("commentTopics", topic));
List<Comment> entries = criteria.list();
throws org.hibernate.exception.DataException: No value specified for parameter 1.
This is the query built:
select this_.comment_id as comment1_0_0_, this_.comment as comment0_0_, commenttop2_.comment_id as comment1_0_2_, commenttop2_.topic as topic2_ from comments this_ left outer join comments_topic commenttop2_ on this_.comment_id=commenttop2_.comment_id where this_.comment_id=?
Am I using incorrect annotations?
Is the criteria query not being constructed properly?
I placed the CommentTopic enum in a CommentTopicWrapper class.
I updated the annotation for the commentTopicsSet to:
#OneToMany(targetEntity = CommentTopicWrapper.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = Constants.COMMENTS_TOPIC_JOIN_TABLE, joinColumns = #JoinColumn(name = "comment_id"), inverseJoinColumns = #JoinColumn(name = "topic"))
private Set<CommentTopicWrapper> commentTopics;
Related
I created two simple entities for trying out the java persistence manytomany mapping. But whatever I try, the jointable won't be populated with a mapping and remains empty.
UserClass:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(targetEntity = Order.class ,fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "users_orders",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "order_id", referencedColumnName = "id")
)
#JsonIgnoreProperties(value = "orderUsers")
private Set<Order> userOrders = new HashSet<>();
}
OrderClass:
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(mappedBy = "userOrders", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "userOrders")
private Set<User> orderUsers = new HashSet<>();
}
I added Getter/Setter/Constructor via Lombok.
Create and save an user. Create an order, add the user and save it. But still the jointable remains empty.
Any ideas?
I am using spring-boot-starter-data-jpa 1.5.1.RELEASE which internally uses hibernate-core 5.0.11.Final
My entity looks like this:
AreaDto
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH)
#JoinColumn(name = "AREA_ROWID")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "FESTIVAL", joinColumns = {
#JoinColumn(name = "AREA_ROWID", referencedColumnName = "AREA_ROWID")}, inverseJoinColumns = {
#JoinColumn(name = "FESTIVAL_ROWID", referencedColumnName = "FESTIVAL_ROWID")})
private Collection<ActionDto> festivalActions;
}
FestivalDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
ActionDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
I'm trying to make sense of the below ideas:
What is the strategy used by hibernate to decide on the festival_rowid (or festival_row ids) used to get all the associated action? How will hibernate generated SQL query vary if i change festivalActions fetch strategies between LAZY and EAGER? I know about proxying, collection proxying and all, my question is specific to how those sql is generated and how it may have an impact on deciding the value of bind parameter.
Is my mapping accurate or should I be using a multimap for this relationship since an area could have multiple festival and each festival could have multiple actions
Background:
I am getting below error which goes away if I change the fetch type from LAZY to EAGER. Hoping to understand the behaviour for gaining some confidence in the fix. I have read SO and error
org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto#280856b5
This mapping does not make much sense. You can't map festivalActions this way because there is no way to persist the state properly through such a mapping. Also festival in AreaDto should be mapped by the area in FestivalDto. Try the following instead:
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
public Collection<ActionDto> getFestivalActions() {
return festival.stream().flatMap(f -> f.actions.stream()).collect(Collectors.toList());
}
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
I rewrite my SpringMVC app with using Hibernate.I try to make query for selecting lectures for group of students by id of group.with using SQL query(before I started rewrite it with using Hibernate)this query was:
"SELECT * FROM lectures WHERE id IN (SELECT lecture_id FROM lectures_groups WHERE group_id =?) ORDER BY date_of_lecture"
I have Lecture and Group etities:
#Entity
#Table(name = "lectures")
public class Lecture {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "lectures_groups", joinColumns = #JoinColumn(name = "lecture_id"), inverseJoinColumns = #JoinColumn(name = "group_id"))
private List<Group> groups = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "teacher_id")
private Teacher teacher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "subject_id")
private Subject subject;
#Column(name = "date_of_lecture")
private LocalDateTime date;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "audience")
private Audience audience;
public Lecture() {
}
//getters setters
}
and:
#Entity
#Table(name = "groups")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "group_name")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cathedra_id", referencedColumnName = "id")
private Cathedra cathedra;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "lectures_groups", joinColumns = #JoinColumn(name = "group_id"), inverseJoinColumns = #JoinColumn(name = "lecture_id"))
private List<Lecture> lectures = new ArrayList<>();
public Group() {
}
//getters setters
}
I tried somthing like:
List<Lecture> lectures = session.createQuery("select l from lectures l join l.groups g where g.id=:groupId")
.setParameter("groupId", group.getId())
.list();
but I get Exception: org.hibernate.hql.internal.ast.QuerySyntaxException: lectures is not mapped
So how can i do it?
In hql query you need to provide the name of the entity in the query instead of the table name. So in your case, you should replace lectures with Lecture in the query.
List<Lecture> lectures = session.createQuery("select l from Lecture l join l.groups g where g.id=:groupId")
.setParameter("groupId", group.getId())
.list();
I have the following classes:
#Entity
#Table(name = "elements")
#Inheritance(strategy=InheritanceType.JOINED)
#XmlRootElement
public abstract class Elements implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idelement")
private Integer idElement;
#Basic(optional = false)
#Column(name = "code")
private String code;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
private Collection<Alarms> alarmsCollection;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
private Collection<ElementsHistorical> elementsHistoricalCollection;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "elementsCollection")
#Fetch(value = FetchMode.SUBSELECT)
private Collection<ElementsGroups> elementsGroupsCollection;
//Constructors, getters and setters
}
#Entity
#Table(name = "alarms")
#XmlRootElement(name = "Alarms")
public class Alarms implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idalarm")
private Integer idAlarm;
#JoinColumn(name = "idelement", referencedColumnName = "idelement")
#ManyToOne(optional = false)
private Elements idElement;
//Constructors, getters and setters
}
I created a jersey webservice and a DAO class with CRUD operations to treat Alarms data. With this implementation, when I call GET method, I get the whole Elements object inside the Alarms one. This is the DAO method called:
public List<Alarms> getAlarms(){
Session session = SessionUtil.getSession();
Query query = session.createQuery("from Alarms");
List<Alarms> alarms = query.list();
session.close();
return alarms;
}
I don't want this fetch = FetchType.EAGER fetch type, as I just need the PK of Elements, but after some research this is the only way I found to make my service work.
I tried this and this approach, but I've not been able to make it work.
You can use left outer join fetch in you query.
Query query = session.createQuery("from Alarms alarm left outer join fetch alarm.idElement element");
Now you can keep fetch = FetchType.LAZY (which is default).
Try to add #XmlTransient annotation to fields in Elements class that you want to ignore.
like this :
#Entity
#Table(name = "elements")
#Inheritance(strategy=InheritanceType.JOINED)
#XmlRootElement
public abstract class Elements implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idelement")
private Integer idElement;
#Basic(optional = false)
#Column(name = "code")
#XmlTransient
private String code;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
#XmlTransient
private Collection<Alarms> alarmsCollection;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "idElement")
#Fetch(value = FetchMode.SUBSELECT)
#XmlTransient
private Collection<ElementsHistorical> elementsHistoricalCollection;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "elementsCollection")
#Fetch(value = FetchMode.SUBSELECT)
#XmlTransient
private Collection<ElementsGroups> elementsGroupsCollection;
//Constructors, getters and setters
}
You can use multiselect with Tuple, for select only specific columns.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<Alarms> root = cq.from(Alarms.class);
CollectionJoin<Object, Object> joinCollection = root.joinCollection("alarmsCollection");
cq.multiselect(joinCollection.get("idElement"));
List<Tuple> tupleResult = entityManager.createQuery(cq).getResultList();
for (Tuple t : tupleResult) {
Long idElement = (Long) t.get(0);
}
I have a database with several entities, in particular Book and User. Between them there exists a ManyToMany relationship like this:
Book:
#Entity
#Table(name = "Books")
public class Book implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "bookId", nullable = false, unique = true)
private Long id;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "price", nullable = false)
private int price;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "bookId"),
inverseJoinColumns = #JoinColumn(name = "userId"))
private Set<UserAccount> users;
User:
#Entity
#Table(name = "UserAccounts")
public class UserAccount implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "userId", nullable = false, unique = true)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "password", nullable = false)
private String password;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "User_Book",
joinColumns = #JoinColumn(name = "userId"),
inverseJoinColumns = #JoinColumn(name = "bookId"))
Set<Book> purchasedBooks;
Everything works fine, the table User_Book is indeed created in the database. The problem seems to be related to the access of this Table.
For example,
Query query = entityManager.createQuery("SELECT u FROM User_Book u");
keeps telling me the following:
The abstract schema type 'User_Book' is unknown
So, shall I create from scratch the User_Book entity? Will it get automtically populated like now, that is, whenever a user buys a book, will this purchase be recorded in the table?
User_Book is not an entity. Therefore you cannot use createQuery, BUT you can use createNativeQuery to execute a SQL query:
Query query = entityManager.createNativeQuery("SELECT * FROM User_Book");
The result will be List<Object[]>