Code from my Entity Role
#Embedded
#LazyCollection(LazyCollectionOption.FALSE)
#CollectionOfElements
#JoinTable(name = "TEST_TABLE", joinColumns = #JoinColumn(name = "ROLE_ID"))
#AttributeOverrides({
#AttributeOverride(name = "code", column = #Column(name = "TSTCODE")),
#AttributeOverride(name = "work", column = #Column(name = "TSTWRK"))
})
private List<TestID> tests;
}
TestID class
#Embeddable
#AccessType("field")
public class TestID implements Serializable
{
private String code;
private String work;
// getters, setters
}
Get exception SQLGrammarException
Caused by: java.sql.SQLException: ORA-00904: "TESTS0_"."WORK": invalid identifier
Entity manager create query that trying get access to columns CODE and WORK instead of TSTCODE and TSTWRk that are in #Column annotations.
Any ideas?
Hibernate-annotation 3.2.1.ga
persistance-api 1.0
jboss-4.2.3.GA
UPDATE:
if rename fields in TestID class to table's columns names, then all warks normally
#Embeddable
#AccessType("field")
public class TestID implements Serializable
{
private String tstcode;
private String tstwks;
Remove #CollectionOfElements and just use #Embedded. I think you're double-mapping it as it is right now. Also, JPA's #ElementCollection is recommended over Hibernate's #CollectionOfElements.
Update: I kinda missed the fact that you're mapping a collection of components. You'll want to add an #Column(name="...") to the fields in your TestID to make it map correctly in that case. Even though it's somewhat contrary to the way embedded components are supposed to work, that's the only way I know to do it.
According to the hibernate recommendations (2.2.5.3.3. Collection of basic types or embeddable objects) you should use
#ElementCollection
#CollectionTable(name="TEST_TABLE", joinColumns = #JoinColumn(name = "ROLE_ID"))
#AttributeOverrides({
#AttributeOverride(name = "code", column = #Column(name = "TSTCODE")),
#AttributeOverride(name = "work", column = #Column(name = "TSTWRK"))
})
Related
I am facing the issue reported as subject when dealing with the following example:
[other irrelevant annotations]
#IdClass(KeyDAO.class)
public class KeyDAO implements Serializable {
#Id
#NotNull
private String KEYGROUP;
#Id
#NotNull
private String KEYVAL;
[...]
#OneToMany(mappedBy = "KEY")
#ToString.Exclude
private List<OrderDateDAO> ODATES;
}
[other irrelevant annotations]
#IdClass(OrderDateDAO.class)
public class OrderDateDAO implements Serializable {
[...]
#Id
#NotNull
private String ODTTYPE;
[...]
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = Constants.DART_KEYGROUP_ODTTYPE, referencedColumnName = "KEYGROUP")),
#JoinColumnOrFormula(column = #JoinColumn(name = "ODTTYPE", referencedColumnName = "KEYVAL", insertable = false, updatable = false))
})
#ToString.Exclude
#OrderBy("ORDERASC ASC")
private KeyDAO KEY;
}
Basically, OrderDateDAO shall be joined with KeyDAO, and both use an #IdClass annotation. By following the tips on this site, I decided to use a JoinColumnsOrFormulas annotation, because:
the real column to join is ODTTYPE <-> KEYVAL
the KEY table shall be narrowed down by setting a specific fixed value for the KEYGROUP column
Currently, I am getting the following exception:
Caused by: org.hibernate.AnnotationException: referencedColumnNames(KEYGROUP, KEYVAL) of eu.unicredit.dtm.dtm_be.dao.oracle.OrderDateDAO.id.KEY referencing eu.unicredit.dtm.dtm_be.dao.oracle.KeyDAO not mapped to a single property
I have tried several other options, but still the error occurs.
Any help or suggestions here?
Thank you.
Regards,
A.M.
I need to authenticate the user on my application, but I'm having some difficulties with the #ElementCollection mapping on the User class.
I'm using the example found on this Spring Security tutorial to build my application on, so most things shown here are quite similar to that.
The User class Set<UserProfile> was originally defined as:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "HRM_USER_USER_PROFILE",
joinColumns = { #JoinColumn(name = "USER_ID") },
inverseJoinColumns = { #JoinColumn(name = "USER_PROFILE_ID") })
private Set<UserProfile> userProfiles = new HashSet<UserProfile>();
// -- getter and setter
The UserProfile class as:
#Column(name="type", length=15, unique=true, nullable=false)
private String type = UserProfileType.USER.getUserProfileType();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
And the UserProfileType enum as:
USER("USER"),
DBA("DBA"),
ADMIN("ADMIN");
String userProfileType;
private UserProfileType(String userProfileType){
this.userProfileType = userProfileType;
}
public String getUserProfileType(){
return userProfileType;
}
Which is already persisted on the database, but the User class can't read.
EDIT - insert the #ElementCollection part
#ElementCollection(targetClass = UserProfile.class)
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "HRM_USER_USER_PROFILE",
joinColumns = { #JoinColumn(name = "id_user") },
inverseJoinColumns = { #JoinColumn(name = "id_profile") })
#Column(name = "user_profiles")
#Cascade(org.hibernate.annotations.CascadeType.ALL)
private Set<UserProfile> userProfiles = new HashSet<UserProfile>(0);
What I noticed is that the user_profiles column on the user table is not created nor any other tables that might refer to this mapping.
Instead of running a SQL script loading UserProfileType, User and their relationship, I'm using a #PostConstruct bean to load default values on application start.
I expected to print out the complete user info but I get no information on the user profiles.
What am I missing here?
Thanks in advance!
I think you have to keep the "Many-to-Many" mapping only on "UserProfile" class, because "ElementCollection" is used to join elements which aren't entities, in your case, you must use the "CollectionTable" annotation.
#ElementCollection
#CollectionTable(name="HRM_USER_USER_PROFILE", joinColumns=#JoinColumn(name="id_profile"))
#AttributeOverrides({
#AttributeOverride(name="type",
column=#Column(name="TYPE"))
})
private Set<UserProfile> userProfiles = new HashSet<UserProfile>(0);
"CollectionTable" annotation indicates the name of your collection and the join column, after that, "AttributesOverrides" annotation set the columns which are part of your collection, in this case, I just put the "Type" column, but you can add more.
...
#AttributeOverrides({
#AttributeOverride(name="type", column=#Column(name="TYPE")),
#AttributeOverride(name="otherColumn", column=#Column(name="OTHER_COLUMN"))
})
...
I hope this information helps you.
Good luck.
I am experiencing a problem with hibernate and lazy loading of objects.
basically I want to load an class which has an eagerly loaded field and not load the lazy fields of child classes
Take the following QuestionVO class
#Entity
#Table(name = "question")
public class QuestionVO extends BaseDAOVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -5867047752936216092L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "questionText", unique = false, nullable = false, length = 4000)
#Size(min = 3, max = 4000)
#Pattern(regexp = MobileAppsRegexConstants.GENERAL_ALLOWED_CHARCHTERS, message = "Question Text Not valid.")
private String questionText;
#ManyToOne(fetch = FetchType.EAGER)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name = "MENU_STYLE_ID", nullable = true)
private MenuStyleVO menuStyle;
}
Take the following MenuStyleVO class
#Entity
#Table(name = "menu_style")
public class MenuStyleVO extends BaseDAOVO implements Serializable{
/**
*
*/
private static final long serialVersionUID = 3697798179195096156L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#Column(name = "menuStyleName", unique = false, nullable = false, length = 200)
private String menuStyleName;
#Column(name = "menuTemplate", unique = false, nullable = false, length = 200)
private String menuTemplate;
#OneToOne(fetch = FetchType.LAZY, optional=false)
#Cascade({ CascadeType.SAVE_UPDATE })
#JoinColumn(name="logo_id")
#JsonProperty("logo")
private ApplicationImageVO logo;
}
And this ApplicationImageVO class
#Entity
#Table(name = "application_image")
public class ApplicationImageVO extends BaseDAOVO implements Serializable {
/**
*
*/
private static final long serialVersionUID = -9158898930601867545L;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image1242x2208")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage1242x2208;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image1536x2048")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage1536x2048;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "image2048x1536")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private SubmissionLauncherImagesVO launcherImage2048x1536;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "logo")
#Cascade({ CascadeType.ALL })
#JsonIgnore
private MenuStyleVO menuStyleLogo;
}
If L load the QuestionVO class from the database using the following hibernate criteria code - all the lazy fields of MenuStyleVO and ApplicationImageVO are also loaded.
On complicated use cases, this results in this query getting very slow
public QuestionVO findMasterAppQuestionById(int id) {
Criteria criteria = currentSession().createCriteria(QuestionVO.class);
criteria.add(Restrictions.eq("id", id));
QuestionVO questionVO = (QuestionVO) criteria.uniqueResult();
return questionVO;
}
What I am wondering is - would it be possible to load the QuestionVO class and its eager fields and tell hibernate to ignore lazy fields from the other classes bar those that are needed?
Cheers
Damien
Last time we faced an issue like this we used a constructor on parent class, which use only the desired fields of determined query.
I can't remember in fully how constructor inside a jpql query works, but it must be something like this:
select new com.package.class(c.field1, c.field2) from com.package.class c
Remember, a constructor with same arguments must be present on the desired entity.
Pros:
- Better query perfomance;
- Can be replicated with other arguments;
Cons:
- Pretty limited, you can only use this hack on the main entity you are querying;
- Includes a constructor only for determined query, poor design;
Also, you should take a look on EnttyGraphs of JPA. Seems quite promising, but didn't work as desired in our project.
Btw, Hibernate has put us many times on performance issues, hope this hack help you, good luck!
Edit:
Why this pattern would help in performance issues?
Basically, with the example i've showed before, you are not loading everything via Hibernate, only the two fields (field1 and field2) of the main entity. Without using a constructor you shoudn't be able to do that, because your query would not result in a collection of the desired entity, but in a collection of two objects each iteration (Object[]). Using the constructor pattern you are creating instances of the desired entity, but only selecting a few fields from database, and that's why this pattern can help you, you are returning a collection of the desired entity with only a few fields.
I am having one hibernate pojo class which has 3 fields specified in #UniqueConstraint (unique together) where one of these 3 fields is nullable=true.
When I try to update entry with session.update(pojo) it updates all the entries in database which matches 2 fields (which are not nullable), so does hibernate avoid nullable fields while querying? or there is something what I should know about it?
Edit: Added class
#Entity
#Table (name = "details",
uniqueConstraints = {#UniqueConstraint(columnNames = {"service_id", "billing_item_id", "service_type_id"}, name="UK_name_it")}
)
public class Detail implements Serializable {
#ManyToOne
#JoinColumn(name = "service_id")
#ForeignKey(name = "FK_name2")
#Id
private Service service;
#ManyToOne
#JoinColumn(name="billing_item_id")
#ForeignKey(name = "FK_name3")
#Id
private BillingItem billingItem;
#ManyToOne
#JoinColumn(name="currency_id")
#ForeignKey(name = "FK_name4")
private Currency currency;
#ManyToOne
#JoinColumn(name="service_type_id")
#ForeignKey(name = "FK_name5")
private ServiceType serviceType;
#Column(name = "completed", nullable = false)
private boolean completed;
}
There doesn't seem to be any option like that to have a nullable field in composite key, so I had to end up by adding a integer autoincrement primary key to the table, and keeping service, billingItem and serviceType fields in #UniqueConstraint.
There is another option I could adopt, which is possible in certain scenarios, by adding a serviceType which is considered as All entry (basically when serviceType is null it applies to all the serviceTypes.) and instead of using null for serviceType point to this entry, this way we can have PK and no need to make serviceType a nullable field.
In a legacy database, I have three tables: Users, Workgroups, and UsersWorkgroup. UsersWorkgroup stores what role a user has in a workgroup.
Here are the relevant code snippets:
#Entity
#Table(name = "users_workgroup")
public class UsersWorkgroup implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected UsersWorkgroupPK usersWorkgroupPK;
#JoinColumn(name = "idworkgroup", referencedColumnName = "idworkgroup")
#ManyToOne(optional = false)
private Workgroup workgroup;
#JoinColumn(name = "user_name", referencedColumnName = "user_name")
#ManyToOne(optional = false)
private Users users;
#Column(name = "role")
private Integer role;
#Embeddable
public class UsersWorkgroupPK implements Serializable {
#Basic(optional = false)
#Column(name = "idworkgroup", insertable=false, updatable=false)
private int idworkgroup;
#Basic(optional = false)
#Column(name = "user_name", insertable=false, updatable=false)
private String userName;
#Entity
#Table(name = "workgroup")
public class Workgroup implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idworkgroup")
private Integer idworkgroup;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idworkgroup")
private Collection<UsersWorkgroup> usersWorkgroupCollection;
And of course, problem is, it doesn't work.
Currently I get this exception:
Exception Description: An incompatible
mapping has been encountered between
[class entity.Workgroup] and [class
entity.UsersWorkgroup]. This usually
occurs when the cardinality of a
mapping does not correspond with the
cardinality of its backpointer.
Which I don't understand since OneToMany should match ManyToOne... Or is it a ManyToMany relationship? If I switch to #ManyToMany, I get this:
Exception Description: The target
entity of the relationship attribute
[workgroup] on the class [class
com.ericsson.rsg.ejb.entity.UsersWorkgroup]
cannot be determined. When not using
generics, ensure the target entity is
defined on the relationship mapping.
I'm trying to understand compound keys (embedded), but all the examples I could find have only simple columns that are not foreign keys (but that's the whole point of a compound key, isn't it?). Can the UsersWorkgroup table secretly be a join table?
Should I declare the PK class as a strict POJO class? Or should I put the #JoinColumn annotations in the PK class? How do I refer to the columns within the compound key from another table? Should I initialize the PK object in the refering class constructor, or is it not necessary?
I feel stuck completely.
First of all, I think your relation is a Many To Many, as a user can be in many groups, and a group can have many users (or I would assume so).
Second, as far as I know you have to reference both id_workgroup and user_name as JoinColumns, because they are part of the PK and a unit, so both should be referenced.
Also, I see the "equals" and "hashCode" methods missing from your embedded PK, as well as the getters/setters. I believe they are mandatory.
Your mapping looks fine except for mappedBy - it should be a property name, not a column name:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "workgroup")
private Collection<UsersWorkgroup> usersWorkgroupCollection;