I want to update a ManyToMany relation in my update statement.
I'm using Reference class, here is the class :
public class Reference implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="user_gen")
#SequenceGenerator(name="user_gen", sequenceName="SEQ_USER")
#Column(name = "DOCUMENTID", nullable = false)
private int documentID;
#Column(name = "REFERENCE")
private String reference = "";
#Column(name = "CODE")
private String code = "";
#Column(name = "REMARK")
private String remark = "";
#Column(name = "TITLE")
private String title = "";
#ManyToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(name = "DOCUMENTS_MACHINES", joinColumns = { #JoinColumn(name = "DOCUMENTID") }, inverseJoinColumns = { #JoinColumn(name = "MACHINEID") })
private Set<Machine> machines = new HashSet<Machine>();
I would like to know if it is possible to update a ManyToMany relations in a HQL statement because session.update(object) would cause conflicts problem because of missing properties.
What I've tried :
String hqlUpdate = "update Reference r " +
"set r.title = :title, " +
"r.machines = :machines " +
"where r.documentID = :id";
int updatedEntities = session.createQuery( hqlUpdate )
.setString("title", reference.getTitle())
.setParameterList("machines", reference.getMachines())
.setInteger("id", reference.getDocumentID())
.executeUpdate();
However, it doesn't work, so my question is : Is it possible to update a collection and how ?
Thanks
Check that many to many hibernate update mapping query example:
many to many hibernate update
Also have a look at CascadeType. You can set the behaviour for save/update operations directly by using the CascadeType in the relationship annotation.
Related
I used Spring Boot and QueryDSL.
When called findAllByWriterGroupByClient method in ClientMemoRepositoryImpl.java, [generated query 1] generated once and [generated query 2] generated several times.
Additionally, when read result of this query as Tuple in ClientMemoServiceImpl.java, [generated query 3] is generated many times.
ClientMemoRepositoryImpl.java
#Override
public List<Tuple> findAllByWriterGroupByClient(String searchKeyword, Long writerId, boolean hasAdminRole) {
QClientMemo qClientMemo1 = new QClientMemo("cm1");
QClientMemo qClientMemo2 = new QClientMemo("cm2");
JPAQuery<Tuple> memoDtoJPAQuery = qf.select(
JPAExpressions.selectFrom(qClientMemo1)
.where(qClientMemo1.clientId.eq(qClientMemo.clientId).and(
qClientMemo1.createdDate.eq(
JPAExpressions
.select(qClientMemo2.createdDate.max())
.from(qClientMemo2)
.where(qClientMemo2.clientId.eq(qClientMemo.clientId))
)
)
),
new CaseBuilder()
.when(qClientMemo.createdDate.gt(LocalDateTime.now().minusDays(7)))
.then(1)
.otherwise(0).sum()
)
.from(qClientMemo);
if ((!hasAdminRole) && writerId != null) {
memoDtoJPAQuery = memoDtoJPAQuery.where(qClientMemo.writer.id.eq(writerId));
}
if (searchKeyword != null)
memoDtoJPAQuery = memoDtoJPAQuery.where(
qClientMemo.title.contains(searchKeyword)
.or(qClientMemo.content.contains(searchKeyword))
.or(qClientMemo.clientId.clientName.contains(searchKeyword))
.or(qClientMemo.writer.name.contains(searchKeyword))
);
return memoDtoJPAQuery
.groupBy(qClientMemo.clientId)
.orderBy(OrderByNull.DEFAULT)
.fetch();
}
generated query 1
select
(select
clientmemo1_.id
from
client_memo clientmemo1_
where
clientmemo1_.client_id=clientmemo0_.client_id
and clientmemo1_.created_date=(
select
max(clientmemo2_.created_date)
from
client_memo clientmemo2_
where
clientmemo2_.client_id=clientmemo0_.client_id
)
) as col_0_0_, sum(case
when clientmemo0_.created_date>? then ?
else 0
end) as col_1_0_
from
client_memo clientmemo0_
group by
clientmemo0_.client_id
order by
null asc
generated query 2
select
[all fields of client_memo entity]
from
client_memo clientmemo0_
where
clientmemo0_.id=?
generated query 3
select
[all fields of client entity]
from
client client0_
where
client0_.id=?
ClientMemoServiceImpl.java
List<Tuple> clientMemos = clientMemoRepository.findAllByWriterGroupByClient(
readClientMemoDto.getSearchKeyword(),
readClientMemoDto.getUserId(),
hasAdminRole
);
clientMemos.forEach(clientMemo -> {
Map<String, Object> result = new HashMap<>();
Integer newCnt = clientMemo.get(1, Integer.class);
if (newCnt != null) {
result.put("newMemoNum", newCnt);
}
MemoDto memoDto = new MemoDto();
ClientMemo memo = clientMemo.get(0, ClientMemo.class);
if (memo != null) {
memoDto.ofClientMemo(memo);
result.put("memoDetail", memoDto);
}
results.add(result);
});
ClientMemo.java
#Entity
#Table(name = "client_memo")
#Getter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#DynamicInsert
public class ClientMemo {
#JsonIgnore
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "title", nullable = false)
private String title;
#Lob
#Column(name = "content")
private String content;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="client_id")
private Client clientId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="writer")
private User writer;
#Column(name = "created_date")
private LocalDateTime createdDate;
#Column(name = "updated_date")
private LocalDateTime updatedDate;
#Column(name = "is_admin")
private boolean isAdmin;
}
Client.java
#Entity
#Table(name = "client")
#Getter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#DynamicInsert
public class Client {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "client_name", nullable = false)
private String clientName;
#Column(name = "client_phone_num", nullable = false)
private String clientPhoneNum;
#Column(name = "service_start_time")
private LocalDateTime serviceStartTime;
#Column(name = "service_end_time")
private LocalDateTime serviceEndTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "media_id")
private Media media;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_charge_id")
private User personCharge;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "normal_memo")
private ClientMemo normalMemo;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "admin_memo")
private ClientMemo adminMemo;
#Column(name = "status", columnDefinition = "varchar(20) default 'UNCONTRACTED'")
#Enumerated(EnumType.STRING)
private ClientStatus status;
#Column(name = "is_deleted", nullable = false)
private boolean isDeleted;
}
All FetchType of Data Relationship are FetchType.LAZY.
I don't understand why occur this problem and why some people say that better using FetchType.LAZY than FetchType.EAGER.
Do I understand QueryDSL or SpringBoot correctly?
Thanks
You should share with us your Jpa entities.
In my opinion, you should have setted some associations in your entity (with #OneToMany, etc..), probably in Eager Mode (which is the default mode).
When you try to load one instance of your object from the database, Hibernate loads the associations as well. In eager mode, hibernate loads the associations by querying the database (which generates additional sql queries).
If you define your associations in Lazy mode, Hibernate will populate your entity jpa with some proxy objects and will fetch the associations later, only when you access it (so that means the sql query of your association is deffered when you only try to access the association in your code).
I have the following entities:
#Entity
#Table(name = "author")
public class Author implements Serializable {
private static final long serialVersionUID = 12345L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "author_id")
private int authorId;
#Column(name = "author_bio")
private String authorBio;
#Column(name = "author_email")
private String authorEmail;
#Column(name = "author_favourite_section")
private String authorFavouriteSection;
#Column(name = "author_password")
private String authorPassword;
#Column(name = "author_username")
private String authorUsername;
#OneToOne(mappedBy = "author", fetch = FetchType.LAZY)
private Blog blog;
#OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
private List<Post> posts;
// getters and setters
#Entity
#Table(name = "blog")
public class Blog implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "blog_id")
private int blogId;
#Column(name = "blog_title")
private String blogTitle;
#OneToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "blog_author_id", unique = true)
private Author author;
#OneToMany(mappedBy = "blog", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Post> posts;
// getters and setters
#Entity
#Table(name = "post")
public class Post implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "post_id")
private int postId;
#Column(name = "post_subject")
private String postSubject;
#Column(name = "post_body")
private String postBody;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "blog_id")
private Blog blog;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "post_author_id")
private Author author;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "post_tag", joinColumns = {
#JoinColumn(name = "post_id", nullable = false, updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "tag_id",
nullable = false, updatable = false)})
private Set<Tag> tags = new HashSet<Tag>();
// getters and setters
#Entity
#Table(name = "tag")
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tag_id")
private int tagId;
#Column(name = "tag_name")
private String tagName;
#ManyToMany(mappedBy = "tags", fetch = FetchType.LAZY)
private Set<Post> posts = new HashSet<Post>();
// getters and setters
The following data is presented in db:
author-blog-post-tag-AND-post_tag-tables
THE MAIN GOAL TO ACHIEVE IS: Find all authors which have written posts that contain appropriate tags.
I can do it using a SQL query:
SELECT a.author_id, a.author_bio, p.post_id, p.post_subject, t.tag_id, t.tag_name from author a
join blog b
on a.author_id = b.blog_author_id
join post p
on p.post_author_id = a.author_id
join post_tag pt
on p.post_id = pt.post_id
join tag t
on t.tag_id = pt.tag_id
where t.tag_name in ('Football', 'Basketball')
And the correct result is returned with author, filtered posts and tags.
But I need to do it using hibernate.
So using hibernate I want to find all authors which have written posts that contain appropriate tags.
And all those authors with ONLY those posts which contain indicated tags (see above - 'Football', 'Basketball') have to be returned.
I wrote this code:
final DetachedCriteria authorCriteria = DetachedCriteria.forClass(Author.class, "author");
authorCriteria.createAlias("author.posts", "post");
authorCriteria.createAlias("post.tags", "tag");
Criterion football = Restrictions.eq("tag.tagName", "Football");
Criterion basketball = Restrictions.eq("tag.tagName", "Basketball");
authorCriteria.add(Restrictions.or(football, basketball));
authorCriteria
.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
final List<Author> result = (List<Author>)getConfiguredHibernateTemplate().findByCriteria(authorCriteria);
and I expected to receive:
Author(author_id = 54) with only one Post (post_id = 26) and this post containing two tags ('Football' and 'Basketball') as I received it using above SQL query.
But the actual result is that I receive Author(author_id = 54) with ALL HIS POSTS PRESENTED IN DB (ERROR AND PROBLEM HERE !!!) and each post contains all tags which also presented in db.
intellij-idea-debug-result
Hibernate generated the following queries:
select this_.author_id as author_i1_0_2_, this_.author_bio as author_b2_0_2_, this_.author_email as author_e3_0_2_, this_.author_favourite_section as author_f4_0_2_, this_.author_password as author_p5_0_2_, this_.author_username as author_u6_0_2_, post1_.post_id as post_id1_2_0_, post1_.post_author_id as post_aut4_2_0_, post1_.blog_id as blog_id5_2_0_, post1_.post_body as post_bod2_2_0_, post1_.post_subject as post_sub3_2_0_, tags5_.post_id as post_id1_2_, tag2_.tag_id as tag_id2_3_, tag2_.tag_id as tag_id1_4_1_, tag2_.tag_name as tag_name2_4_1_ from author this_ inner join post post1_ on this_.author_id=post1_.post_author_id inner join post_tag tags5_ on post1_.post_id=tags5_.post_id inner join tag tag2_ on tags5_.tag_id=tag2_.tag_id where (tag2_.tag_name=? or tag2_.tag_name=?)
select blog0_.blog_id as blog_id1_1_0_, blog0_.blog_author_id as blog_aut3_1_0_, blog0_.blog_title as blog_tit2_1_0_ from blog blog0_ where blog0_.blog_author_id=?
select posts0_.post_author_id as post_aut4_0_0_, posts0_.post_id as post_id1_2_0_, posts0_.post_id as post_id1_2_1_, posts0_.post_author_id as post_aut4_2_1_, posts0_.blog_id as blog_id5_2_1_, posts0_.post_body as post_bod2_2_1_, posts0_.post_subject as post_sub3_2_1_ from post posts0_ where posts0_.post_author_id=?
How do I achieve the expected and correctly filtered result using hibernate?
You asked for an author which writes a blog about Football or BaketBall:
DetachedCriteria.forClass(Author.class, "author");
It happens that this author also wrote blogs about something else. So you get what you've asked for. In your sql statement you ask for a projection whereas with hibernate you ask the ORM to get the object (author) with its posts collection.
I tried using projection ( authorCriteria.setResultTransformer(CriteriaSpecification.PROJECTION )
final DetachedCriteria authorCriteria = DetachedCriteria.forClass(Author.class, "author");
authorCriteria.createAlias("author.posts", "post");
authorCriteria.createAlias("post.tags", "tag");
final Criterion football = Restrictions.eq("tag.tagName", "Football");
final Criterion basketball = Restrictions.eq("tag.tagName", "Basketball");
authorCriteria.add(Restrictions.or(football, basketball));
authorCriteria.setResultTransformer(CriteriaSpecification.PROJECTION);
final List<Author> result = (List<Author>) getConfiguredHibernateTemplate().findByCriteria(authorCriteria);
and the following result I see in debugger:
Ok that is correct and I can analyze those data and sorted them to Author->List of Posts -> with Tags to specific Post. But this is extra work in code.
I assume maybe hibernate has more elegant way to return filtered data I need or NOT. If no way then I became disappointed of hibernate. Then it is more convenient to use some spring jdbc template or mybatis or something like that.
Using approach "You could then select Post as your root entity" involves extra queries to db. Why we need to do so extra work ? Looks like hibernate is not flexible and useful suffice if it can not run query which I want and return results in way I prefer.
I have Test entity :
public class Test {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "duration", nullable = false)
private int duration;
#Column(name = "test_name", nullable = false, unique = true)
private String testName;
#Column(name = "archived", nullable = false)
private boolean archived;
#OneToMany(mappedBy = "test", fetch = FetchType.EAGER)
private Set<Question> questions;
#ManyToMany(mappedBy = "tests")
private Set<User> users;
Question Entity:
public class Question {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "is_multichoice", nullable = false)
private boolean isMultichoice;
#Column(name = "is_open", nullable = false)
private boolean isOpen;
#Column(name = "picture")
private String picture;
#Column(name = "question")
private String question;
#ManyToOne
#JoinColumn(name = "test_id", nullable = false)
private Test test;
#Column(name = "archived", nullable = false)
private boolean isArchived;
#OneToMany(mappedBy = "question", fetch = FetchType.EAGER)
private Set<Answer> answers;
This Test entity has Set of questions, in such way Question Entity has Set of answers.
I wrote SQL query to get test (The reason why it is not HQL you could find by link Hibernate HQL : no entity found for query) :
#NamedNativeQuery(name = "getCurrentTestById",
query = "SELECT t.id as tId, t.test_name, t.duration, q.id as qId, " +
"q.question as question, q.is_multichoice as is_multichoice, " +
"q.is_open as is_open, a.id as aId, a.answer_text as answer_text FROM result r " +
"JOIN test t ON r.test_id = t.id " +
"JOIN user u ON r.user_id = u.id " +
"JOIN question q ON t.id = q.test_id JOIN answer a ON q.id = a.question_id " +
"WHERE t.id = :testId AND u.id = :userId AND r.permission = :permissionId " +
"AND q.archived = false AND a.archived = false")
Now i need to map it to my entity Test by using #SqlResultSetMapping annotation:
#SqlResultSetMappings({
#SqlResultSetMapping(name="toTest",
entities = {
#EntityResult(entityClass = com.bionic.entities.Test.class, fields = {
#FieldResult(name = "id", column = "tId"),
#FieldResult(name = "test_name", column = "test_name"),
#FieldResult(name = "duration", column = "duration"),
#FieldResult(name = "questions.question", column = "question"),
#FieldResult(name = "questions.id", column = "qId"),
#FieldResult(name = "questions.isMultichoice", column = "is_multichoice"),
#FieldResult(name = "questions.isOpen", column = "is_open"),
#FieldResult(name = "questions.answers.id", column = "aId"),
#FieldResult(name = "questions.answers.answer_text", column = "answer_text"),
})
})
})
I am getting exception :
Caused by: org.hibernate.MappingException: dotted notation reference neither a component nor a many/one to one
This is why frameworks are generally bad news. Instead of using hibernate, you should follow the interface segregation principle. Your application should not know or care about How to select the data you need and what the table names are etc. Simply create a stored procedure that takes this responsibility and call it as opposed to having all the junk code in your app. Then if you want an easy way to map just have your stored proc return json by calling For JSON at the end. Mapping object fields to the JSON object becomes a piece of cake. You will find that with frameworks you spend more time troubleshooting the framework than actually programming.
I have been reading and watching tons of tutorials and just can't seem to get the many to many relationship to work. I have built a new project to simplify the project down to one many to many relationship. Please take a look at the code below and lend some suggestions as to why this fails. Currently I get a null pointer at the *keywd.getMaterialRecordList().add(record); line. If I comment this out then I get the same null pointer on the next .get....add();. If it matters I have abandoned Derby and moved to an H2 database.
#Entity
#Table(name = "MATERIAL_RECORD")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "MaterialRecord.findAll", query = "SELECT m FROM MaterialRecord m")})
public class MaterialRecord implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
#Column(name = "SDS_NUMBER")
private Integer sdsNumber;
#Column(name = "PRODUCT_NAME")
private String productName;
#ManyToMany(mappedBy = "materialRecordList")
private List<Keywords> keywordsList;
#Entity
#Table(name = "KEYWORDS")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Keywords.findAll", query = "SELECT k FROM Keywords k")})
public class Keywords implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "KEY_ID")
private Integer keyId;
#Column(name = "KEYWORD_NAME")
private String keywordName;
#JoinTable(name = "KEYWORD_LOOKUP", joinColumns = {
#JoinColumn(name = "KEY_ID", referencedColumnName = "KEY_ID")}, inverseJoinColumns = {
#JoinColumn(name = "SDS_NUMBER", referencedColumnName = "SDS_NUMBER")})
#ManyToMany
private List<MaterialRecord> materialRecordList;
public class JpaTest {
public static void main (String [] args){
MaterialRecord record = new MaterialRecord();
record.setProductName("oofbar");
Keywords keywd = new Keywords();
keywd.setKeywordName("testing");
Keywords keywd2 = new Keywords();
keywd2.setKeywordName("testing2");
record.getKeywordsList().add(keywd);
record.getKeywordsList().add(keywd2);
keywd.getMaterialRecordList().add(record);
keywd2.getMaterialRecordList().add(record);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("JavaApplication26PU");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(record);
em.persist(keywd);
em.persist(keywd2);
tx.commit();
Ok here are my thoughts.
To avoid the null pointer exception as the entity is not retrieved from the persistence context the Collection in this case the list has not been initialized, so use this.
#ManyToMany(mappedBy = "materialRecordList")
private List<Keywords> keywordsList = new ArrayList<Keywords>();
Or if you dont want to use that approach use setter method to create and set the list before try to fill it. That will avoid null pointer exceptions and your example should work. =)
This only is needed as you are creating your own entity from scratch, If you use find that list will be filled and created automatically.
I think the problem is with relation. ManyToMany relation need to be with primary keys of two table.
try this :
#JoinTable(name = "KEYWORD_LOOKUP", joinColumns = {
#JoinColumn(name = "KEY_ID", referencedColumnName = "KEY_ID")}, inverseJoinColumns = {
#JoinColumn(name = "SDS_NUMBER", referencedColumnName = "ID")})
#ManyToMany
private List<MaterialRecord> materialRecordList;
update me if you still get some problem.
Im stuck with this problem. The database schema is provided by someone else so I cant simply change names. I tried add everywhere proper annotations, maybe I'm missing something (obvious)?
Here is my full mapping (quite many classess), I'll ommit getter/setters.
The problem is when hibernate is trying to get all List<ControlRuleAttrib> controlRuleAttribs
Controle Rule
#Entity
#Table(name = "CONTROL_RULE")
public class ControlRule implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CONTROL_RULE_ID")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade(CascadeType.ALL)
#JoinColumn(name = "CONTROL_RULE_TYPE_ID")
#ForeignKey(name = "CONTROL_RULE_TYPE_ID")
private ControlRuleType controlRuleType;
#Column(name = "JOB_NM")
private String jobname;
#Column(name = "LIBRARY_NM")
private String libraryname;
#Column(name = "TABLE_NM")
private String tablename;
#Column(name = "COLUMN_NM")
private String columnname;
#OneToMany(fetch = FetchType.LAZY)
#Cascade(CascadeType.ALL)
#JoinTable(name = "CONTROL_RULE_ATTRIB", joinColumns = {
#JoinColumn(name = "CONTROL_RULE_ID", nullable = false, updatable = false)
})
private List < ControlRuleAttrib > controlRuleAttribs;
}
ControlRuleAttrib
#Table(name = "CONTROL_RULE_ATTRIB")
#Entity
public class ControlRuleAttrib {
#EmbeddedId
private ControlRuleAttribPK controlRuleAttribPK;
#Column(name = "ATTRIBUTE_VALUE")
private String attributeValue;
}
ControleRuleAttribPK
Question here is, is it possible to somehow get Entity ControlRuleAttribType from ControlRuleAttrib? As you can see below ControlRuleAttribTypeId is the id of ControleRuleAttribType. I'd like to get whole object isteand of integer.
#Embeddable
public class ControlRuleAttribPK implements Serializable {
#Column(name = "CONTROL_RULE_ID")
private Long controlRuleId;
#Column(name = "ATTRIBUTE_SEQ_NUM")
private Integer attributeSeqNum;
#Column(name = "CONTROL_RULE_ATTRIB_TYPE_ID")
private Integer controlRuleAttribTypeId;
}
ControleRuleAttribType
#Entity
#Table(name = "CONTROL_RULE_ATTRIB_TYPE")
public class ControlRuleAttribType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CONTROL_RULE_ATTRIB_TYPE_ID")
private Integer id;
#Column(name = "CONTROL_RULE_ATTRIB_TYPE_NM")
private String typename;
#Column(name = "CONTROL_RULE_ATTRIB_TYPE_DESC")
private String typedesc;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade(CascadeType.ALL)
#JoinColumn(name = "CONTROL_RULE_TYPE_ID")
#ForeignKey(name = "CONTROL_RULE_TYPE_ID")
private ControlRuleType controlruletype;
}
ControleRuleType
#Entity
#Table(name = "CONTROL_RULE_TYPE")
public class ControlRuleType implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CONTROL_RULE_TYPE_ID")
private Integer id;
#Column(name = "CONTROL_RULE_TYPE_NM")
private String typename;
#Column(name = "CONTROL_RULE_TYPE_DESC")
private String typedesc;
}
EDIT
Here is stacktrace:
https://gist.github.com/a30dd9ce534d96bb9a97
As you'll find out, it fails here:
at
com.execon.controllers.main.MainPageController.getMainPage(MainPageController.java:33)
[classes:]
and this is it:
List<ControlRule> list = SessionFactoryUtils.openSession(
sessionFactory ).createQuery( "from ControlRule" ).list();
System.out.println( list );
every object which mapping I added, has toString() method declared like this:
#Override
public String toString()
{
String s = "ControlRule{";
s += "id=" + id.toString();
s += ", controlRuleType=" + controlRuleType;
s += ", jobname='" + jobname + '\'';
s += ", libraryname='" + libraryname + '\'';
s += ", tablename='" + tablename + '\'';
s += ", columnname='" + columnname + '\'';
s += ", controlRuleAttribs=" + controlRuleAttribs;
s += '}';
return s;
}
And hibernate request:
https://gist.github.com/c8584113522757a4e0d8/4f31dc03e7e842eef693fa7ba928e19d27b3ca26
Help please :)
EDIT 2
Well after reading #Jens answer, I did some changes in the code. First I did as you wrote and it gave error:
org.hibernate.AnnotationException: A Foreign key refering
com.execon.models.controlrules.ControlRuleAttrib from
com.execon.models.controlrules.ControlRule has the wrong number of
column. should be 3
I guess this is right, as I have composite primary key.
Then I tried it this way:
#OneToMany(fetch = FetchType.LAZY)
#Cascade(CascadeType.ALL)
#JoinTable(name = "CONTROL_RULE_ATTRIB",
joinColumns = {
#JoinColumn(name = "CONTROL_RULE_ID", nullable = false, updatable = false)
},
inverseJoinColumns = {
#JoinColumn(name = "CONTROL_RULE_ID", nullable = false, updatable = false),
#JoinColumn(name = "CONTROL_RULE_ATTRIB_TYPE_ID", nullable = false, updatable = false),
#JoinColumn(name = "ATTRIBUTE_SEQ_NUM", nullable = false, updatable = false)
})
private List<ControlRuleAttrib> controlRuleAttribs;
Quite close but it gives me the following exception:
Repeated column in mapping for collection..
So finally I removed
joinColumns =
{
#JoinColumn(name = "CONTROL_RULE_ID", nullable = false, updatable = false)
}
And everything compiled except that when I try to reach collection, Hibernate is doing following query:
https://gist.github.com/c88684392f0b7a62bea5
The last line, is controlrul0_.CONTROL_RULE_CONTROL_RULE_ID=? while it should be controlrul0_.CONTROL_RULE_ID=?.
Is there anyway I can make it work? :/
After struggling with this for last few hours I finally make it working within my project. The thing I did was this:
ControlRule
#OneToMany(fetch = FetchType.LAZY, mappedBy = "controlRuleAttribPK.controlRuleId")
#Cascade(CascadeType.ALL)
private List<ControlRuleAttrib> controlRuleAttribs;
Basically pointing that the collection should use controlRuleId from composite primary key. So far its working great!
I do not clearly understand which identifier is too long, but I can propose that you can try to change #Id annotated field types from Integer to Long. And it will be great to get some detailed info (stack trace etc.) about your problem.
The problem is the column
CONTROL_RULE_ATTRIB.controlRuleAttribs_CONTROL_RULE_ATTRIB_TYPE_ID
Which is so long it can't possibly exist in your database. I don't see in the question any hint how it is actually named, but I am assuming the proper name is
CONTROL_RULE_ATTRIB.CONTROL_RULE_ATTRIB_TYPE_ID
So the question is: Why is hibernate trying to access this column. The table it is in is a mapping table which is mapped in hibernate as:
#OneToMany(fetch = FetchType.LAZY)
#Cascade(CascadeType.ALL)
#JoinTable(name = "CONTROL_RULE_ATTRIB", joinColumns = {#JoinColumn(name = "CONTROL_RULE_ID", nullable = false, updatable = false)})
private List<ControlRuleAttrib> controlRuleAttribs;
If you look at the #JoinTable annotation you'll notice that it defines the table name as it is used and a join column. But a mapping table has two sets of join columns. The second on is not specified. Therefore the NamingStrategy you configured or if you didn't configured any the default is used. Just specify the other set of join columns using the attribute inverseJoinColumns and all should be fine.
The complete annotation should look like:
#JoinTable(name = "CONTROL_RULE_ATTRIB", joinColumns = {#JoinColumn(name = "CONTROL_RULE_ID", nullable = false, updatable = false)}, inverseJoinColumns = {#JoinColumn(name = "CONTROL_RULE_ATTRIB_TYPE_ID")})
Note: I have no idea if you need any of the nullable, updatable stuff on that column as well.
See also the documentation of the JoinTable annotation