Hibernate One To Many criteria doesn't work - java

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.

Related

I don't now why occur N+1 Problem using QueryDSL

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).

How to avoid updating child tables when using save Hibernate?

I have a parent table called USER and a child table called USERDATA linked with OneToMany. When I go to use the save method, if the child record exists it is updated. I would like it not to be updated but not added. What am I doing wrong?
My classes:
#Entity
#Table(name="USER")
#IdClass(UserPK.class)
public class User implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name="USERID")
private String userId;
#Id
#Column(name="USERNUMBER")
private String userNumber;
private String name;
private String surname;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<UserData> userDatas;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(value = FetchMode.SUBSELECT)
private List<OtherData> otherDatas;
//getter and setter
}
UserData:
#Entity
#Table(name="USERDATA")
public class UserData implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private String id;
private String City;
private String University;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "USERID", referencedColumnName = "USERID"),
#JoinColumn(name = "USERNUMBER", referencedColumnName = "USERNUMBER")
})
private User user;
//getter and setter
OtherData:
#Entity
#Table(name="OTHERDATA")
public class OtherData implements Serializable{
private static final long serialVersionUID = 1L;
#Id
private String id;
private String hobby;
private String religion;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "USERID", referencedColumnName = "USERID"),
#JoinColumn(name = "USERNUMBER", referencedColumnName = "USERNUMBER")
})
private User user;
//getter and setter
And my save:
session.save(obj); //obj is and User object
when I do the first insert everything is ok, when I do it again by changing the data of the primary key of user but not quelly of the primary key of userData or otherData, the data is updated. I don't want them updated.
You can not reuse the same Java object in this case. Also, changing the primary key should produce a big fat warning when flushing that object Hibernate.
If you want a new row to be inserted, you have to create a new object with new User(). Another option is to let Hibernate forget about the old object by using session.detach(user). The problem is, that Hibernate knows the object already and due to that, tries to update the existing row when you call save again.
Try setting the column like so:
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(insertable = false, updatable = false)
private List<UserData> userDatas;
I haven't tested it but it should block inserts and updates via the entity. Of course you will need separate queries to insert update UserData somehow.
BTW I suggest not to use FetchType.EAGER, but always use FetchType.LAZY. If you need the child data to be fetched, just adapt the query you are using to do a FETCH JOIN, see here for an example:
SELECT FROM User u LEFT JOIN FETCH u.userData d
Hi Fesilox Please write this example
#JoinColumn(name = "USERID", referencedColumnName = "USERID", nullable = false,
insertable=false, updatable=false)

Hibernate filter apply every time entity is loaded

I am trying to write a multilanguage software. Using filters in hibernate, I want that every time an entity is accessed, only translation with the specific language being load.
#Entity
#Table(name = "province")
#FilterDef(name = "findByLanguage", parameters = {#ParamDef(name = "language", type = "string")})
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "country_id", nullable = false)
private Country country;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "province_id")
#Filter(name = "findByLanguage", condition = "language = :language")
private Set<ProvinceTranslation> translations;
}
This works as I expected for province, But Country has also a list of translations, and I want that CountryTranslation list is also contains only specified language when I load a province. But this code does not work:
#Entity
#Table(name = "country")
#FilterDef(name = "findByLanguage", parameters = {#ParamDef(name = "language", type = "string")})
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "country_id")
private List<Province> provinces;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "country_id")
#Filter(name = "findByLanguage", condition = "language = :language")
private Set<CountryTranslation> translations;
}
So if I load Country directly everything is ok, But if I load Province and access Country inside it, loading Country is different and #Filter is being ignored. How can I achieve this?

What hibernate / jpa annotation is required

I am trying to create a new User(entity1) - it has reference to a Group (entity2) via a link table Member (entity3)
A user has a Set of groups as a class variable.
When i create my user object i want to say this user will be a member of group n (there are pre defined users that are linked to by id (1,2,3,4,5,6...) each group has some associated data in the table.
Whenever I create my user object as follows;
User user = new User();
user.setActive(1);
user.setCrby("me");
user.setUsername("username");
user.setCrdate("2016-06-20 12:42:53.610");
user.setCrwsref("...");
user.setModby("...");
user.setModdate("2016-06-20 12:42:53.610");
user.setModswref("..");
user.setBackground("Y");
user.setPassword("password");
user.setFullName("me");
Group group = new Group();
group.setId(1);
Group group2 = new Group();
group2.setId(2);
Set<Group> sets = new HashSet<Group>();
sets.add(group);
sets.add(group2);
user.setGroups(sets);
userDao.addUser(user);
I keep getting errors telling me that certain columns cannot be null. What I actually want to happen here is not to be doing an insert in to the group table but associating a user to a line in the group table. Is there a particular way I can prevent the columns in the group table being modified? I think I need to modify the mappings between the link table - this is how much pojos link right now
User
#Entity
#Table(name = "user")
public class User
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "zmember", joinColumns = {#JoinColumn(name = "username")}, inverseJoinColumns = {#JoinColumn(name = "id")})
private Set<Group> groups = new HashSet<Group>(0);
Member link table
#Entity
#Table(name = "member")
public class Member implements Serializable
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Id
#Column(name = "sgpid")
private int sgpid;
#Column(name = "username")
private String memberUsername;
Group
#Entity
#Table(name = "group")
public class Group
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
What is happening is there is no association to the link Member table so ideally should User have a set of member objects rather than a set of groups?
Thanks - this was quite hard to explain so sorry if it is hard to understand
This is a typical case for the #ManyToMany annotation. See for example:
https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html
The relationship from User to Group is essentially ManyToMany. You could model this is using the #ManyToMany annotation however one drawback with this approach is you cannot save additional information about the group in the join table such as 'date_joined'.
See: https://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#ManyToMany
Using this approach you would not need the Join entity Member and the relationship on User would look like:
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "zmember", joinColumns = {#JoinColumn(name = "member_id", referencedColumnName = "id")}, inverseJoinColumns = {#JoinColumn(name = "group_id", referencedColumnName = "id")})
private Set<Group> groups = new HashSet<Group>(0);
The alternative to using #ManyToMany is to use a Join entity Member(ship) as you have done. This would allow you to save additional data about the relationship (by defining additional field mappings in the Join entity).
In this case the mappings would look like:
User:
public class User{
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Membership> memberships = new HashSet<Membership>(0);
//if required, you can 'hide' the join entity from client code by
//encapsulating add remove operations etc.
public void addToGroup(Group group){
Membership membershup = new Membership();
membership.setUser(this);
membership.setGroup(group);
memberships.add(membership);
)
public Set<Groupp> getGroups(){
//iterate memberships and build collection of groups
}
}
Membership:
public class Membership{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#ManyToOne
#JoinColumn(name = "user_id")
private Member member;
#ManyToOne
#JoinColumn(name = "group_id")
private Group group;
}
Group:
#Entity
#Table(name = "group")
public class Group
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#OneToMany(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Membership> memberships = new HashSet<Membership>(0);
}

JPA Many to Many trouble

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.

Categories