I have a M:N relationship between table Users and Groups. Now I am trying to join those two tables using JPA and I always get this exception:
Multiple writable mappings exist for the field [GROUPS.name]. Only one may be defined as writable, all others must be specified read-only.
Here is my Users class (I didn't enclose getters and setters for brevity), it's implemented by Admin class and SignedUser class with some additional properties.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Users implements Serializable {
#Id
#Column(name = "login", nullable = false, length = 10)
private String login;
#Column(name = "name", nullable = false, length = 30)
private String name;
#Column(name = "surname", nullable = false, length = 50)
private String surname;
#Column(name = "email", nullable = false, length = 100)
private String email;
#Column(name = "password", nullable = false, length = 20)
private String password;
#ManyToMany(mappedBy="users")
private List<Groups> groups;
and this is the Group class:
#Entity
public class Groups implements Serializable {
#Id
#Column(name = "name", nullable = false, length = 20)
private String groupName;
#Column(name = "name", nullable = false, length = 50)
private String descr;
#ManyToMany
#JoinTable(name = "user_group",
joinColumns = {#JoinColumn(name = "groupName")},
inverseJoinColumns = {#JoinColumn(name = "login")})
private List<Users> users;
I've also tried to put JoinTable annotation into Users class but it ended with the same result. Thanks in advance for any advices.
In the class Group you have two mappings for column name as properties groupName and descr.
The error is not related to the ManyToMany.
Related
I want to join a single column from another table to one of my #Entity classes:
Currently it works as follows:
#Entity
public class Product {
#Id
private long id; //autogenerated
String type; //used for mapping
#ManyToOne
#JoinColumn(name = "type", insertable = false, updatable = false)
ProductMapping mapping;
}
#Entity
public class ProductMapping {
#Id
String type;
String longname;
}
Question: how could I replace the #ManyToOne mapping to directly map to String longname?
//TODO: how to directly map to 'mapping.longname'?
#JoinColumn(name = "type", insertable = false, updatable = false)
String mapping.longname;
You can use #Formula annotation with a query like this :
#Formula("(select pm.longname from product_mapping pm where pm.COL_NAME = value)")
private String longName;
//give the column name of type from product_mapping table
OR
You can also use the below approach :
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "type", referencedColumnName = "COL_NAME", insertable = false, updatable = false)
ProductMapping mapping;
The other entity use #NaturalId annotation on the field.
#Entity
public class ProductMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
String type;
#NaturalId
#Column(name = "SOME_VALUE")
String longname;
}
I have 2 tables project and employee have multiple relationship. Project has many employees, employee can join many projects( many to many). project have one employee whose is team leader, an employee can manager many projects(many to one). So how do I design database, and classes model mapping with database. Some one help me
This is my code
class user
#Entity(name = "USERS")
public class Users {
#Id
#Column(name = "USER_ID", length = 6)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
#Column(name = "USER_NAME", length = 50, nullable = false)
private String userName;
#Column(name = "PASS_WORD", length = 50, nullable = false)
private String passWord;
#Column(name = "FULL_NAME", length = 50, nullable = false)
private String fullName;
#Column(name = "EMAIL", length = 50, nullable = false)
private String email;
#Column(name = "PHONE", length = 11, nullable = true)
private String phone;
#Column(name = "STATUS", nullable = true)
private Boolean status;
#ManyToMany(fetch = FetchType.LAZY)
#JsonIgnore
#JoinTable(name = "USERPROJECT", joinColumns = { #JoinColumn(name =
"USER_ID") }, inverseJoinColumns = {
#JoinColumn(name = "PROJECT_ID") })
private List<Project> projects;
#OneToMany(mappedBy = "teamlead")
private List<Project> projectOfTeamLead;
//get set.....
}
class project
#Entity(name = "PROJECTS")
public class Project {
#Id
#Column(name = "PROJECT_ID", length = 6)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer projectId;
#Column(name = "PROJECT_NAME", length = 50, nullable=false)
private String projectName;
#Column(name = "USER_CREATE_ID", length = 6, nullable=false)
private Integer userCreateId;
// #Column(name = "TEAM_LEAD_ID", length = 6, nullable=false)
// private Integer teamLeadId;
#Column(name = "TIME_START", nullable=true)
private Date timeStart;
#Column(name = "TIME_END", nullable=true)
private Date timeEnd;
#ManyToMany(mappedBy = "projects")
private List<Users> users;
#ManyToOne
#JoinColumn(name = "TEAM_LEAD_ID")
private Users teamlead;
//get set...
}
I think you need to make your database fit into 3nf (third normal form). You are describing a database that uses two tables, but I think you need three at least, that way you can describe what employees are on what project. A "team" table that uses the PK's from the other two tables should be used. Conventionally, you would name it something like "ProjectEmployees" as it is comprised of the PK's from the "Projects" table and the "Employees" table respectively. I hope that answers your question. If you don't understand what I'm talking about, I highly recommend you watch this.
Ok, so I'd like to implement a simple forum example. So, I have threads, messages and users, of course and these are the pojos (I omitted the usually getters and simplicity)
Message
#Entity
#Table(name = "message")
public class Message implements java.io.Serializable, RecognizedServerEntities
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name = "thread", nullable = false)
private Thread thread;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name = "author", nullable = true)
private User user;
#Column(name = "title", nullable = false, length = 31)
private String title;
#Column(name = "body", nullable = false, columnDefinition = "Text")
private String body;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_modified_date", nullable = false, length = 19)
private Date lastModifiedDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_date", nullable = false, updatable = false, length = 19)
private Date createdDate;
}
User
#Entity
#Table(name = "user", uniqueConstraints =
{ #UniqueConstraint(columnNames = "email"),
#UniqueConstraint(columnNames = "nick") })
public class User implements java.io.Serializable, RecognizedServerEntities
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "email", unique = true, nullable = false, length = 31)
private String email;
#Column(name = "password", nullable = false, length = 31)
private String password;
#Column(name = "nick", unique = true, nullable = false, length = 31)
#NaturalId(mutable = false)
private String nick;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "registered_date", nullable = false, updatable = false, length = 19)
private Date registeredDate;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
private Set<Thread> threads = new HashSet<Thread>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
private /**transient /**/ Set<Message> messages = new HashSet<Message>(0);
}
Thread
#Entity
#Table(name = "thread")
public class Thread implements java.io.Serializable, RecognizedServerEntities
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({CascadeType.SAVE_UPDATE})
#JoinColumn(name = "parent_thread", nullable = true)
private Thread parentThread;
#ManyToOne(fetch = FetchType.LAZY)
#Cascade({CascadeType.SAVE_UPDATE})
#JoinColumn(name = "author", nullable = true)
private User user;
#Column(name = "title", nullable = false, length = 63)
private String title;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "last_modified_date", nullable = false, length = 19)
private Date lastModifiedDate;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_date", nullable = false, updatable = false, length = 19)
private Date createdDate;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "thread"/**/, orphanRemoval = true/**/)
#Cascade({ CascadeType.REMOVE })
private /**transient /**/ Set<Message> messages = new HashSet<Message>(0);
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parentThread", orphanRemoval = true)
#Cascade({CascadeType.REMOVE })
private /**transient /**/ Set<Thread> subThreads = new HashSet<Thread>(0);
}
I have many doubts on the annotations of course, but these are the relevant choice.
When I delete an user, I don't want to delete all his threads and messages, so it make sense to don't use orphan-removal or cascade delete on the #OneToMany associations (ie the messages and threads collections).
Also, because the id is automatically generated from the database, I don't think it make sense at all to use the annotation CascadeType.UPDATE (or SAVE_UPDATE) on the collections of all the entity.
A thread are the most problematic entity to manage. When we delete a thread, we want that all its subthreads and all its messages were deleted. So, I use the CascadeType.REMOVE and orphan-removal annotations.
An all the #ManyToOne associations, I use the CascadeType.ALL. The idea is that if we delete a message or a subthread, all the parents will be updated.
All the collections are not transient.
Feel free to propose suggestion on this of course.
Btw, given the whole story, this is the question: suppose I have a thread "mThread" started from the user "mUser" with many messages from different users, how can I safely delete the user?
I tried different things, but I'm not sure of anything and in most cases I only have exceptions.
EDIT
I also have another class, StorageManager<T>, that is used to encapsulate the common code between entities. Basically, it implements the "one session per transaction" pattern. So each methodX() basically:
invoke sessionFactory.openSession() and session.beginTransaction()
invoke session.methodX()
invoke transaction.commit()
invoke session.clear() and session.close
Example with code
for (Thread t : mUser.getThreads())
{
t.setUser(null);
storageManagerThread.update(t);
}
for (Message m : mUser.getMessages())
{
m.setUser(null);
storageManagerMessage.update(t);
}
storageManagerUser.delete(mUser);
Until this point, all the table in the database have the right values. However, I don't know if it is the right way to proceed, because it leaves dirty collections.
Indeed, when at later point I try to execute some other options (e.g. update(mThread) or delete a message from mThread) a NullPointerException was thrown. Why is this? .
I have a user management application that allocate each user a team and one or many access to different application. Now for the reporting page I am trying to fetch data from two table (UserInfo & UserAppAccess) by Hibernate but I can't.
Here are the tables :
Table 1 (UserInfo):
#Entity
#Table(name = "user_info", uniqueConstraints = { #UniqueConstraint(columnNames = "username"), #UniqueConstraint(columnNames = "email") })
public class UserInfo implements java.io.Serializable {
public enum UserStatus {
active, inactive
}
public enum UserType {
user, creator
}
private static final long serialVersionUID = 2650114334774359089L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 100)
private String id;
#Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
#Column(name = "password", nullable = false, length = 80)
private String password;
#Column(name = "status", nullable = false, length = 10)
#Enumerated(EnumType.STRING)
private UserStatus status;
#Column(name = "type", nullable = false, length = 10)
#Enumerated(EnumType.STRING)
private UserType type;
#Column(name = "phone", nullable = true, length = 30)
private String phone;
#Column(name = "email", nullable = true, length = 50)
private String email;
#Column(name = "first_name", nullable = true, length = 50)
private String firstName;
#Column(name = "last_name", nullable = true, length = 50)
private String lastName;
#Column(name = "login", nullable = true, length = 100)
private long login;
#Column(name = "alert", nullable= true, length=500)
private String alert;
#OneToOne
#JoinColumn(name = "team_id")
private Team team;
}
Table 2 (Team):
#Entity
#Table(name = "team", uniqueConstraints = { #UniqueConstraint(columnNames = "team_name"), #UniqueConstraint(columnNames = "team_code") })
public class Team implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#Column(name = "team_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "team_code", unique = true, nullable = false, length = 10)
private String code;
}
Table 3 (Access):
#Entity
#Table(name = "access_def")
public class Access implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#Column(name = "access_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "access_code", unique = true, nullable = false, length = 10)
private String code;
}
Table 4 (Application):
#Entity
#Table(name = "application", uniqueConstraints = { #UniqueConstraint(columnNames = "name") })
public class Application implements java.io.Serializable {
private static final long serialVersionUID = 5803631085624275364L;
#Id
#Column(name = "name", nullable = false, length = 100)
private String name;
}
Table 5 (UserAppAccess):
#Entity
#Table(name = "user_app_access")
#Embeddable
public class UserAppAccess implements java.io.Serializable {
private static final long serialVersionUID = 7933770163144650730L;
#Id
#Column(name = "id", unique = true, nullable = false, length = 80)
private String id;
#OneToOne
#JoinColumn(name = "user_id")
private UserInfo userInfo;
#Column(name = "app_name", nullable = false, length = 100)
private String appName;
#OneToOne
#JoinColumn(name = "access_id")
private Access access;
}
I have a report page that allow Admin to select multiple options (for example: list all active users in Team test and application APP1).
here is my code to fetch the data but it is not working :
public List<?> getReport(String teamId,String appName,UserStatus active,UserStatus inactive) {
Session session = sessionFactory.getCurrentSession();
String hql = "SELECT u.firstName,u.username,u.status,u.lastName,u.phone,u.team From UserInfo u,AppAccess a WHERE u.status =? OR u.status =? AND u.team.id = ? AND a.appName = :appName ";
Query query = session.createQuery(hql);
query.setParameter(0, active);
query.setParameter(1, inactive);
query.setParameter(2, teamId);
query.setParameter("appName", appName);
System.out.println(query.list());
return query.list();
}
For instance when I pass
Active Users: Active
inactive User:null
team:test
application :app1
teamId :28f66133-26c3-442b-a071-4d19d64ec0aeappName :app1active :activeinactive:null
I am getting this back from my return query.list();
[[Ljava.lang.Object;#2961116f, [Ljava.lang.Object;#23bfa3a2, [Ljava.lang.Object;#7a8ff303, [Ljava.lang.Object;#9b88d2, [Ljava.lang.Object;#6333934d, [Ljava.lang.Object;#4f0bd71c, [Ljava.lang.Object;#125797cf, [Ljava.lang.Object;#34afa071, [Ljava.lang.Object;#764e75bc, [Ljava.lang.Object;#1913c652, [Ljava.lang.Object;#61413e5a, [Ljava.lang.Object;#264b898, [Ljava.lang.Object;#22930462, [Ljava.lang.Object;#6204cfa9, [Ljava.lang.Object;#29dd9285, [Ljava.lang.Object;#11be6f3c, [Ljava.lang.Object;#6d78d53d, [Ljava.lang.Object;#17f7cff1, [Ljava.lang.Object;#e74e382, [Ljava.lang.Object;#1c047338, [Ljava.lang.Object;#68286fe6, [Ljava.lang.Object;#36ca9a76, [Ljava.lang.Object;#2f62d514, [Ljava.lang.Object;#1932c5a, [Ljava.lang.Object;#6544c984, [Ljava.lang.Object;#70a2d0d, [Ljava.lang.Object;#2d13b417, [Ljava.lang.Object;#6894691f, [Ljava.lang.Object;#6781a7dc, [Ljava.lang.Object;#7133919a]
I'd suggest using native SQL and JDBC for reporting (see
How should I use Hibernate Mapping while dealing with huge data table)
For performance reasons it is desirable to create view model objects from result set right in the DAO. It may looks like mixing levels of abstraction (view layer with persistence layer), but it's ok when you need to fetch a big amounts of data and don't want to make unnecessary object transformations from persistence models to view models.
If you're want to stuck with hibernate, you may define a syntetic entity and map if on a view, containing only necessary columns from multiple:
#Entity
#Table("V_USER_REPORT")
public class UserAppData {
// columns from table "user"
#Id
#Column(name = "id", unique = true, nullable = false, length = 100)
private String id;
#Column(name = "username", unique = true, nullable = false, length = 50)
private String username;
// columns from table "user"
#Column(name = "app_name", nullable = false, length = 100)
private String appName;
// columns from table "team"
#Column(name = "team_id", unique = true, nullable = false, length = 80)
private String team_id;
#Column(name = "team_name", unique = true, nullable = false, length = 100)
private String name;
#Column(name = "team_code", unique = true, nullable = false, length = 10)
private String code;
// and so on...
}
Then you fetch such entity by parameters as you do it with normal entity.
By adding LEFT JOIN FETCH or FETCH ALL PROPERTIES. This will force JOINS insteed of lazy initialization
String hql = "SELECT u.firstName,u.username,u.status,u.lastName,u.phone,u.team From UserInfo u,AppAccess a FETCH ALL PROPERTIES WHERE u.status =? OR u.status =? AND u.team.id = ? AND a.appName = :appName ";
More information can be found in HQL Documentation
Firstly: I hope each of your entity classes have a toString() method (can be auto-generated with eclipse) so you can print them. Printing object reference isn't enough to infer whether/not you're getting what you want.
Secondly, the syntax of HQL joins is normally like this:
String queryString = "select distinct f from Foo f inner join foo.bars as b" +
" where f.creationDate >= ? and f.creationDate < ? and b.bar = ?";
(taken from How do you create a Distinct query in HQL)
[using JPA, MySQL, MVC, Servlets, JSP]
If I read some data from database LEFT JOIN-ing three tables (inside method of DAO object) how should I format that result, so i can set it as request attribute (in servlet) and forwards to JSP page?
Entities(tables in db):
Post:
#Entity
#Table(name = "post")
public class Post implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "post_id", unique = true, nullable = false)
private Integer id;
#Column(name = "post_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "post_date", unique = false, nullable = false)
private Date date;
#Column(name = "post_summary", length=1000, unique = false, nullable = true)
private String summary;
#Column(name = "post_content", length=50000, unique = false, nullable = false)
private String content;
#Column(name = "post_visitors", unique = false, nullable = false)
private Integer visitors;
#ManyToOne
#JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
private User user;
#ManyToOne
#JoinColumn (name = "category_id", referencedColumnName="category_id", nullable = false)
private Category category;
#OneToMany(cascade = { ALL }, fetch = LAZY, mappedBy = "post")
private Set<Comment> comments = new HashSet<Comment>();
...
Entity Comment:
#Entity
#Table(name = "comment")
public class Comment implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "comment_id", unique = true, nullable = false)
private Integer id;
#Column(name = "comment_title", length=300, unique = false, nullable = false)
private String title;
#Column(name = "comment_date", unique = false, nullable = false)
private Date date;
#Column(name = "comment_content", length=600, unique = false, nullable = false)
private String content;
#ManyToOne
#JoinColumn (name = "user_id", referencedColumnName="user_id", nullable = false)
private User user;
#ManyToOne
#JoinColumn (name = "post_id", referencedColumnName="post_id", nullable = false)
private Post post;
...
Entity User:
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer id;
#Column(name = "user_name", length=45, unique = false, nullable = false)
private String name; // "first_name" + ' ' + "last_name"
//URL address to user's image
#Column(name = "user_image", length=500, unique = false, nullable = true)
private String image;
#Column(name = "user_username", length=45, unique = false, nullable = false)
private String username;
#Column(name = "user_password", length=45, unique = false, nullable = false)
private String password;
...
So, I would like to make a method, probably inside PostDAO object that will look something like this:
public <SomeDataTypeFormat???> getPostsSummaries(){
Query q = em.createNativeQuery("SELECT
post_title,
post_summary,
post_date,
COUNT(comment_id) AS comment_cnt,
user.user_name
FROM
post
LEFT JOIN user USING(user_id)
LEFT JOIN comment USING(post_id)
GROUP BY
post_id
ORDER BY
comment_cnt DESC");
...
}
Method returns some fields from all three tables in database.
Do I need to make separate class and store those data in objects of that class? Or JSON (although I haven't worked with it yet)?
What is the practice? What is easiest data format to use and forward from servlet to JSP, for some fields gotten as a result of joining couple tables?
It depends on your objective; to get the data to the browser, JSON and AJAX isn't a bad choice. To get the data to the JSP (from the Servlet), you'll probably want a Data Transfer Object (or possibly an immutable Value Object).