I am working on a relationship in spring jpa and facing this issue. I am trying to establish relationship between a subuseraccess table and subuservehicleaccess table.
Now a subuser can have access to multiple vehicles and each vehicle will have a set of access privileges for the sub user.(eg: read,write).
The private List<SubUserVehicleAccessFeatureList> property in the first class defines the list of the relation class added later viz SubUserVehicleAccessFeatureList.
Error
I am trying to insert record in these tables and, while I am getting an error sql error 1364 sqlstate hy000 hibernate saying the field subUserAccessFeatureId doesn't have a default value.
I tried to debug and the data while saving is all correct. On the contrary if I remove the #JoinColumn(name = "SUBUSERACCESSFEATUREID")) annotation from the below property private List<SubUserVehicleAccessFeatureList> it works as expected. As a part of convention, we need to have names without underscores and hibernate generates underscored names by default.
Referred this but didn't find a solution.
#Entity
#Table(name = "SUBUSERACCESSFEATURELIST")
public class SubUserAccessFeatureList extends TableMetaDataDTO {
/** The sub user access feature id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SUBUSERACCESSFEATUREID")
private Long subUserAccessFeatureId;
/** The sub user id. */
#Column(name = "SUBUSERID")
private Long subUserId;
/** The vehicle id. */
#Column(name = "VEHICLEID")
private Long vehicleId;
/** The vehicle access feature list. */
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "SUBUSERVEHICLEACCESSFEATURELIST", joinColumns = #JoinColumn(name =
"SUBUSERACCESSFEATUREID"))
private List<SubUserVehicleAccessFeatureList> vehicleAcccessFeatureList = new ArrayList<SubUserVehicleAccessFeatureList>();
/** The account access feature list. */
#Column(name = "FEATUREID")
#JoinTable(name = "SUBUSERACCOUNTACCESSFEATURELIST", joinColumns = #JoinColumn(name = "SUBUSERID"))
#ElementCollection(fetch = FetchType.LAZY)
private Set<Long> accountAccessFeatureList = new HashSet<Long>();
//Getter setter eliminated
}
The relation class
#Entity
#Table(name = "SUBUSERVEHICLEACCESSFEATURELIST")
public class SubUserVehicleAccessFeatureList {
/** The sub user vehicle feature id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SUBUSERVEHICLERELATIONID")
private Long subUserVehicleFeatureId;
/** The vehicle id. */
#Column(name = "VEHICLEID")
private Long vehicleId;
/** The feature id. */
#Column(name = "FEATUREID")
#JoinTable(name = "SUBUSERVEHICLERELATIONTABLE", joinColumns = #JoinColumn(name = "SUBUSERVEHICLERELATIONID"))
#ElementCollection(fetch = FetchType.LAZY)
private Set<Long> featureId = new HashSet<Long>();
}
Related
iam working on an app where the association between entities is as follows . Here comment is the owner and iteration and user are the inverse tables . The requirement is -
A user can have many comments similarly a comment can be given by multiple users .
Also an iteration can have multiple comments and a single comment can belong to multiple iterations .
The code is as follows -
Comments entity -
#Table(name="RCOMMENTS")
#Entity
public class RComment{
#Id
#Column(name="COMMENTID")
#GeneratedValue(strategy=GenerationType.AUTO)
private long commentid;
#Column(name="DESCRIPTION")
private String description;
#Column(name="TYPE")
private String type;
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "COMMENTS_USERS", joinColumns = { #JoinColumn(name = "COMMENTID") },
inverseJoinColumns = { #JoinColumn(name = "USERID") })
private Set <RUsers> users = new HashSet<RUsers>();
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "COMMENTS_ITERATIONS", joinColumns = { #JoinColumn(name = "COMMENTID") },
inverseJoinColumns = { #JoinColumn(name = "ITERATIONID") })
private Set <RIteration> iteration = new HashSet<RIteration>();
Users-
#Entity
#Table(name = "RUSER")
public class RUsers {
#Id
#Column(name = "USERID")
#GeneratedValue(strategy=GenerationType.AUTO)
private long userid;
#Column(name = "USERNAME")
private String username;
#Column(name = "PASSWORD")
private String password;
#ManyToMany(mappedBy="users")
private Set <RComment> comment ;
Iteration -
#Table(name = "RITERATION")
#Entity
public class RIteration {
#Id
#Column(name = "iterationid")
#GeneratedValue(strategy=GenerationType.AUTO)
private long iterationid;
private Date startdate;
private Date enddate;
private long iter;
#ManyToMany(mappedBy="iteration")
private Set <RComment> comment = new HashSet<RComment>();
Test code -
RUsers user = (RUsers) session.get(RUsers.class, this.userid);
Set<RComment> comments = user.getComment();
Now the issue that iam facing is the mapping tables are empty .
Also user.getComment() yields empty set
Before i start my web app my db entity tables prepopulated with dummy values . But during run time when i debug the code user.getcomment returns empty set .
Could anyone please help me out what could be the issue .
If you have an bidirectional relationship between your Entities, then Hibernate will pay attention read only one of the two sides when it stores them in the database.
The side of the relation that is the important one, is the one that's ManyToMany annotation does NOT contain the mappedBy variable. And that is the side where you need to assign the value:
RUsers user = ...
RComment comment = ...
comment.users.add(user); <--- that is the assignment hat hibernate will store!
user.comments.add(comment); <--- that assigment will NOT been stored by hibernate, but hibernate will load it from database
I have two tables in my database USERS and ADDRESSES, each user can have many addresses.
I have build entity classes with NetBeans wizard, and it create the classes well:
#Entity
#Table(name = "USERS")
#XmlRootElement
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
// Some fields.......
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private Collection<Address> addressCollection;
public User() {
}
// Getters and Setters.......
}
#Entity
#Table(name = "ADDRESSES")
#XmlRootElement
public class Address implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected AddressPK addressPK;
// Some fields.......
#JoinColumn(name = "USER_ID", referencedColumnName = "ID", insertable = false, updatable = false)
#ManyToOne(optional = false)
private User user;
public Address() {
}
// Getters and Setters.......
}
#Embeddable
public class AddressPK implements Serializable {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private int id;
#Basic(optional = false)
#NotNull
#Column(name = "USER_ID")
private int userId;
public AddressPK() {
}
}
In the session I save the User instance of the current logged in user. But the User's addressCollection never updates when I change the database like:
Address newAddr = new Address();
// Sets values to newAddr......
AddressFacade addrDao = new AddressFacade();
addrDao.create(newAddr); // Succeeded
LoginManager.getCurrentUser().getAddressCollection(); // Returns the old address list.
How can I refresh the current user's instance to get the correct addressCollection?
First, when you have a bidirectional relationship, JPA requires that you keep both sides of the relationship in synch with each other. This allows caching entities and other performance enhancements to be enabled by many providers. In this case, when you set the USER_ID field, you should update the User's addressCollection that is affected by the change so that your object model stays in synch with what you are committing to the database.
An alternative is to force a refresh manually on the User instance. This can be done with a em.refresh(user) call, or through provider specific options and query hints. This is usually the least performant option though as it requires a database hit that isn't needed.
I have the following tables :
#Entity
#Table(name = "CUSTOMER")
public class Customers implements Serializable {
private static final long serialVersionUID = -5419345600310440297L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "cust")
#SequenceGenerator(name = "cust", sequenceName = "cust_ID_SEQ")
#Column(name = "CUSTOMER_ID")
private Long id;
#Column(name = "NAME")
private String name;
#OneToMany(mappedBy = "customer", cascade = CascadeType.PERSIST)
private Set<CustomerDeal> customerDeals;
//getters and setters goes here ....
}
#Entity
#Table(name = "DEALS")
public class Deals implements Serializable {
private static final long serialVersionUID = -7197428343863081750L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "deals_seq")
#SequenceGenerator(name = "deals_seq", sequenceName = "DEALS_SEQ")
#Column(name = "DEAL_ID")
private Long dealId;
#Column(name = "DEAL_NAME")
private String dealColName;
//getters setters
}
#Entity
#Table(name = "CUSTOMER_DEALS")
public class CustomerDeals implements Serializable {
private static final long serialVersionUID = -4249326793843278525L;
#EmbeddedId
private CustomerDealId customerDealId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CUSTOMER_ID", insertable = false, updatable = false)
private Customers customers;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "DEAL_ID", insertable = false, updatable = false)
private Deals deals;
//getters setters
}
#Embeddable
public class CustomerDealId implements Serializable {
private static final long serialVersionUID = 9086627167426987610L;
#Column(name = "DEAL_ID")
private Long dealId;
#Column(name = "CUSTOMER_ID")
private Long customerId;
}
however when I try to create a new customer
Customer cust - new Customer ()
cust.setName("Foo")
CustomerDeals custDeals = new CustomerDeals()
Set<CustomerDeal> custDealsSet = new HashSet<CustomerDeal>
CustomerDeal custDealsSet1 = new CustomerDeal()
CustomerDeal custDealsSet2 = new CustomerDeal()
custDealsSet1.setDeals(dealsRepository.findOne(1))//getting existing deal
custDealsSet1.customers(cust)
custDealsSet2.setDeals(dealsRepository.findOne(2))//getting existing deal
custDealsSet2.customers(cust)
custDealsSet.add(custDealsSet1)
custDealsSet.add(custDealsSet2)
cust.setCustomerDeals(custDealsSet)
customerRepository.saveAndFlush(cust)
customerRepository.saveAndFlush(cust)
I am getting
org.hibernate.id.IdentifierGenerationException: null id generated
for:class CustomerDeal
This is not duplication of this question
Your code that throws exception does not make sense so I guess it is not real code.
CustomerDeal has composite key, so you would not be able to retrieve it with dealsRepository.findOne(1), which means that you probably were retrieveing Deal not CustomerDeal but then the part would never compile:
Set<CustomerDeal> custDealsSet = new HashSet<CustomerDeal>();
custDealsSet.add(dealsRepository.findOne(1))
So, apart from that, I guess you were retrieving the existing deals. And you made a new customer. As the key of CustomerDeal depeneds on both customer and deal, both custumer and deal have to be set before persisting it which you probably forgot to do (and you got your exception). So it should look like:
Customer cust - new Customer ();
cust.setName("Foo");
CustomerDeals custDeal = new CustomerDeals();
custDeal.setCustomer(cust);
custDeal.setDeal(dealsRepository.findOne(1));
cust.getCustomerDeals().add(custDeal);
custDeal = new CustomerDeals();
custDeal.setCustomer(cust);
custDeal.setDeal(dealsRepository.findOne(2));
cust.getCustomerDeals().add(custDeal);
customerRepository.saveAndFlush(cust);
Now you are probably still in trouble. If you override the equals and hash on CustomerDeal so they are ID based (which typical code generator for entities does), both new CustomerDeals instances have them as null, so when you add them to the set the second one will override the first inserted (as null ids will be equals).
You also need to inform JPA that the ID will come from the relations.
In your CustomerDea you need to add #MapsId annotation (on both joins), like:
#MapsId("customerId")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CUSTOMER_ID", insertable = false, updatable = false)
private Customers customers;
Finally, unless your CustomerDeal table contains additional apart from CUSTOMER_ID and Deal_ID, then, it is a simple joint table and should not be mapped at all. That way you will save yourself a lot of trouble.
The reason why you got the error mentioned above is due to a mapping issue(I cant figure out what exactly is wrong though). As a completely different approach, I have modified your mappings. I have tested this and it is working fine. The advantage for you with this mapping is that it makes the CustomerDeals class redundant. Please note that I have removed the sequences as I am using MySQL.
#Entity
#Table(name = "CUSTOMERS")
public class Customer implements Serializable {
private static final long serialVersionUID = -5419345600310440297L;
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "CUSTOMER_ID")
private Long id;
#Column(name = "NAME")
private String name;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(
name="CUSTOMER_DEALS",
joinColumns = #JoinColumn( name="CUSTOMER_ID"),
inverseJoinColumns = #JoinColumn( name="DEAL_ID")
)
private Set<Deals> deals = new HashSet<Deals>();
//Setters and Getters to follow
}
The Deals Class will be
#Entity
#Table(name = "DEALS")
public class Deals implements Serializable {
private static final long serialVersionUID = -7197428343863081750L;
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "DEAL_ID")
private Long dealId;
#Column(name = "DEAL_NAME")
private String dealColName;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "deals")
private Set<Customer> customers = new HashSet<Customer>(0);
//Setters and Getters here
}
Finally the main method which does the insert.
Customer customer = new Customer();
customer.setName("NewCust2");
Deals deals = new Deals();
deals.setDealColName("Deal2");
customer.getDeals().add(deals);
customerRepository.save(customer);
Have a problem persisting a ManyToMany relationship mapped like that
Document.java
public class Document {
.......
#ManyToMany(targetEntity = Category.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "fideuram_gup_documents_in_categories",
joinColumns = #JoinColumn(name="fk_document"),
inverseJoinColumns = #JoinColumn(name = "fk_category"))
private Set<Category> categories = new HashSet<Category>();
.......
}
where Category is one more entity of my model which I don't paste here since it doesn't carry a reverse mapping of this relation, and has just an ID and a name.
When I try to persist Document however I get the following error:
org.hibernate.PropertyAccessException: could not get a field value by reflection getter of it.ardesia.fideuram.gup.model.Category.id
I've surfed the web about it but no page relates to ManyToMany relations. Of course all the ManyToOne relations I have on the entity Document work fine.
I'm using:
spring-data-jpa:1.2.0.RELEASE
hibernate-core:4.2.2.Final
hibernate-entitymanager:4.2.2.final
UPDATE
All entities expose a default constructor and getter/setter for every field. Or,more preciselt, I'm using Spring Roo for creating the entity and it injects getters and setters automatically upon compilation.
You can instrument Hibernate how it must access your property using #javax.persistence.Access annotation; put on your mapped class with #Access.value set to
AccessType.FIELD for direct field access
AccessType.PROPERTY for accessing properties using accessors
Maybe it can help you, I already did the same, I put my code, it creates a join table:
#Entity
#Table(name = "custom_pizza")
public class CustomPizza extends BaseEntity {
private static final long serialVersionUID = 1L;
// ManyToMany instead of oneToMany in order to don't have the unique
// constraint on each primary key of the join table
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "custom_pizza_topping", joinColumns = #JoinColumn(name = "custom_pizza_id"), inverseJoinColumns = #JoinColumn(name = "topping_id"))
private Set<Topping> toppings = new HashSet<Topping>();
public void addTopping(Topping topping) {
toppings.add(topping);
}
public void removeTopping(Topping topping) {
toppings.remove(topping);
}
...
And my topping:
#Entity
#Table(name = "topping")
public class Topping extends BaseEntity {
private static final long serialVersionUID = 1L;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "price", nullable = false)
private float price;
....
and the BaseEntity
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
...
I have three entities User, Center and Profile, so a user has different profiles depending on the center he's in. So we have a table which links the three entities called *user_center_profile*.
Then we have another entity, called Permission which is linked to Profile, so a profile has several permissions.
Because the main entity is User, I have it maped like this:
#Entity
#Table(name = "profile")
public class Profile implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idprofile")
private Long idProfile;
#Column(name = "codprofile")
private String codProfile;
#Column(name = "desprofile")
private String desProfile;
#OneToMany
#JoinTable(name = "profile_permission", joinColumns = { #JoinColumn(name = "idProfile") }, inverseJoinColumns = { #JoinColumn(name = "idPermission") })
private List<Permission> permissions = new ArrayList<Permission>();
/* getters and setters */
}
#Entity
#IdClass(CenterProfileId.class)
#Table(name = "user_center_profile")
public class CenterProfile implements Serializable {
#Id
#ManyToOne
private Center center;
#Id
#ManyToOne
private Profile profile;
/* getters and setters */
}
#Embeddable
public class CenterProfileId implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
#JoinColumn(name = "idCenter")
private Center center;
#ManyToOne
#JoinColumn(name = "idProfile")
private Profile profile;
/* getters, setters, equals, hashcode */
}
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idUser")
private Long idUser;
#Column(name = "codUser")
private String codUser;
/* other properties */
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_center_profile", joinColumns = { #JoinColumn(name = "idUser") }, inverseJoinColumns = {
#JoinColumn(name = "idCenter"), #JoinColumn(name = "idProfile") })
private List<CenterProfile> centersProfileUser = new ArrayList<CenterProfile>();
#Transient
private Center selectedCenter;
/* getters, setters */
}
The thing is that at certain point I have to collect all the permissions that a certain user has... I tried several things and I got lazy load errors, no session or session closed errors, problem loading simultaneous bags...
I even tried to write a plain SQL query and I got the same error...
I can't see the way to build a DetachedCriteria for this, and I don't know if it will give an exception too..
My app can connect to different "centers", when he logs in he can choose which center he wants to connect to, but also once logged in, he can change centers... So when he changes it, I have to recalculate his permissions... that's why I need to get that list of permissions..
How could I get this done in a proper way?
In the end, I built the query like this:
HashSet<Permission> set = new HashSet<Permission>();
for (CenterProfile cp : usuario.getCentersProfileUsuario()) {
// First we build subcriteria to return all the permissions for a certain profile
DetachedCriteria criteriaProfile = DetachedCriteria
.forClass(Profile.class);
criteriaProfile.add(Restrictions.eq("idProfile", cp.getProfile()
.getIdProfile()));
criteriaProfile.createAlias("permissions", "permission");
criteriaProfile.setProjection(Property.forName("permission.idPermission"));
// Then we build criteria for permissions, which should match the results given by subcriteria
DetachedCriteria criteria = DetachedCriteria
.forClass(Permission.class);
criteria.add(Property.forName("idPermission").in(criteriaProfile));
List<Permission> permissions = getHibernateTemplate().findByCriteria(
criteria);
set.addAll(permissions);
}