I'm currently trying to create a many to many mapping using eclipselink. Please note that for this specific example no table to resolve the relation is used (I kwon that this is not a good practice but it is necassary for this specific example).
I've created a database schema and the tables employee3 and insurance to map. The employee table holds a column called insurance_id which is part of the primary key in order to create the mapping. Same goes for insurance with employee_id.
Now for the code:
Here is the code for the two classes:
First off Employee:
#Entity
#Table(name="employee3", schema = "many_to_many")
public class EmployeeManyToMany
{
protected EmployeeManyToMany()
{
}
#Id
private int id;
private String firstName;
private String lastName;
#ManyToMany(cascade = CascadeType.PERSIST)
private Collection<InsuranceManyToMany> insurance;
public EmployeeManyToMany(int id, String firstName, String lastName)
{
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
insurance = new ArrayList<InsuranceManyToMany>();
}
.....
And Insurance:
#Entity
#Table(name = "insurance", schema = "many_to_many")
public class InsuranceManyToMany
{
#Id
#Column(name = "id")
private int insuranceId;
private String company;
#ManyToMany(mappedBy = "insurance", cascade = CascadeType.PERSIST)
private Collection<EmployeeManyToMany> employee;
protected InsuranceManyToMany()
{
}
public void addEmployee(EmployeeManyToMany emp)
{
employee.add(emp);
}
public InsuranceManyToMany(String company, int insuranceId)
{
this.insuranceId = insuranceId;
this.company = company;
employee = new ArrayList<EmployeeManyToMany>();
}
....
After I create an Employee object and add a list of insurances to it i try to persist it into the database.
Which results in the following error:
javax.persistence.RollbackException: Exception [EclipseLink-4002]
Eclipse Persistence Services - 2.7.3.v20180807-4be1041):
org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: null value
in column "insurance_id" violates not-null constraint
Detail: Failing row contains (1, hans, test, null).
Error Code: 0
Call: INSERT INTO many_to_many.employee3 (ID, FIRSTNAME, LASTNAME) VALUES
(?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(swd_ws18_06_Tag3.EmployeeManyToMany#88d98e)
I have no idea why this occures since the values are never null.
Any help is appreciated!
BR
Simon
Related
I am working on a Spring Boot portal using Spring Data JPA and Hibernate mapping and I am finding some difficulties trying to understand how exactly works the following code implemented by someone else (it works fine but JPA\Hibernate are not my cup of tea and I am missing something).
On the database I have this portal_user table representing the users of my application
As you can see this table contains the parent_id field that is a FK of the portal_user table itself. This is used to create a recursive relation between an user and its parent (you can see it is a classical refferal relationship: the parent is the user who bring another user into the system).
This portal_user DB table was mapped on this User entity class:
#Entity
#Table(name = "portal_user")
#Getter
#Setter
public class User implements Serializable {
private static final long serialVersionUID = 5062673109048808267L;
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
#NotNull(message = "{NotNull.User.firstName.Validation}")
private String firstName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "surname")
#NotNull(message = "{NotNull.User.surname.Validation}")
private String surname;
#Column(name = "sex")
#NotNull(message = "{NotNull.User.sex.Validation}")
private char sex;
#Column(name = "birthdate")
#NotNull(message = "{NotNull.User.birthdate.Validation}")
private Date birthdate;
#Column(name = "tax_code")
#NotNull(message = "{NotNull.User.taxCode.Validation}")
private String taxCode;
#Column(name = "e_mail")
#NotNull(message = "{NotNull.User.email.Validation}")
private String email;
#Column(name = "pswd")
#NotNull(message = "{NotNull.User.pswd.Validation}")
private String pswd;
#Column(name = "contact_number")
#NotNull(message = "{NotNull.User.contactNumber.Validation}")
private String contactNumber;
#Temporal(TemporalType.DATE)
#Column(name = "created_at")
private Date createdAt;
#Column(name = "is_active")
private boolean is_active;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
#JsonManagedReference
private Set<Address> addressesList = new HashSet<>();
#ManyToMany(cascade = { CascadeType.MERGE })
#JoinTable(
name = "portal_user_user_type",
joinColumns = { #JoinColumn(name = "portal_user_id_fk") },
inverseJoinColumns = { #JoinColumn(name = "user_type_id_fk") }
)
Set<UserType> userTypes;
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
String email, String pswd, String contactNumber, Date createdAt, boolean is_active) {
super();
this.firstName = firstName;
this.middleName = middleName;
this.surname = surname;
this.sex = sex;
this.birthdate = birthdate;
this.taxCode = taxCode;
this.email = email;
this.pswd = pswd;
this.contactNumber = contactNumber;
this.createdAt = createdAt;
this.is_active = is_active;
}
}
In particular this relationship seems to be handled by this field:
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
Then I have this service method used to insert a new object into the portal_user table taking into account the fact that the parent_id field could be set:
#Override
#Transactional
public User insertClientUser(User clientUser) throws DuplicateException, SubAgentUserNotExist, NotFoundException {
String subAgentEmail = null;
if(clientUser.getParent() != null) subAgentEmail = clientUser.getParent().getEmail();
User checkClientUserExist = this.getUserByemail(clientUser.getEmail());
if (checkClientUserExist != null) {
String MsgErr = String.format("User %s already registered in the system !!! "
+ "Impossible to use POST", clientUser.getEmail());
log.warning(MsgErr);
throw new DuplicateException(MsgErr);
}
log.info(String.format("UserServiceImpl --> insertClientUser client user: %s %s - subagent email: %s ",
clientUser.getFirstName(), clientUser.getSurname(), subAgentEmail));
// check if present because we could add a user without assigned subagent
if(subAgentEmail != null) {
User subAgentUser = this.getUserByemail(subAgentEmail);
if (subAgentUser == null) {
String errorMessage = String.format("Subagent user %s doesn't exist in the sistem !!! "
+ "Impossible to use POST", subAgentEmail);
log.warning(errorMessage);
throw new SubAgentUserNotExist(errorMessage);
}
clientUser.setParent(subAgentUser);
}
User insertedClientUser = userRepository.save(clientUser);
return insertedClientUser;
}
NOTE: the client is the user that I am inserting as a new record of the portal_user DB table while the subAgentUser is an user that yet exist into this DB table and that will be the parent of the user that I am inserting.
So basically, on my portal_user table I will have a new record (the client user) having the parent_id that will contain the ID of the subagent user.
How this method works is pretty simple:
Check if the current user that I have to insert doesn't exist into the portal_user table. If the user doesn't yet exist into the table it means that it have to be inserted.
It retrieve the subagent user (the parent) calling another service method and set it as parent property of the client user that we are inserting.
Finnally it save this client user into the portal_user DB table.
Ok it all pefrectly works but I can not understand how Hibernate is correctly setting the value of the parent_id field of this new inserted method. This parent_id field contains the PK of the parent object (the retrieved subAgentUser object.
If I explore my portal_user table after the execution of the previous service method I found what I expect:
The last row is the inserted object. As you can see the value of the parent_id FK field is 53 that is the PK of the expected parent user in the same table.
It works fine but I cannot understand how. Who said to Hibernate how to set the value of this FK? In particular I am confuserd because it seems that there is not a explicit mapping to this field into my User entity class, infact I have:
#ManyToOne(fetch = FetchType.EAGER)
#JsonProperty("subagent")
private User parent;
So what am I missing? How it exactly work?
In Hibernate, #ManyToOne specifies a single-valued association to another entity class that has many-to-one multiplicity. In this context, it is associated with the same entity.
ORM maps the data from an object model to a relational model and vice versa. So the relationships are mapped using the entities. This helps us to traverse from parent to child objects easily. We can build multiple nested relationships and can be queried or create the objects by traversing using dot and chaining method.
As we know that each entity has its own lifecycle and once the association is set, Hibernate maps the foreign key with the primary identifier of the associated entity.
Here it assigns to the id column which is a primary key. However, it can be customized using #JoinColumn annotation.
Below is the sql that is generated by Hibernate after creating portal_user table to set the association.
alter table portal_user
add constraint FKdgqt4pnsjho6u58mtnackj4h8
foreign key (parent_id)
references portal_user
When subAgentUser entity is set to clientUser as below, hibernate maps the record to the primary identifier of the associated entity(subAgentUser) using parent_id column while flushing the query.
clientUser.setParent(subAgentUser);
Boot,Jpa and hibernate to persist a one-many relation between venues and events.
The error i'm retrieving is
org.h2.jdbc.JdbcSQLException: NULL not allowed for column "VENUE_LOCATION"; SQL statement:
insert into event (id, date, description, media_ref, num_ratings, performer, performer_media, title, total_rating) values (null, ?, ?, ?, ?, ?, ?, ?, ?) [23502-192]
I've tried saving the parent(Venue) class first and exclusively but that produces the same error.
Venue
public class Venue
{
#Id
private String name;
#Id
private String location;
#OneToOne(mappedBy = "venue",cascade = CascadeType.ALL)
#JoinColumn(name="id")
private VenueUser venueUser;
private String mediaRef;
private int rating;
#OneToMany(fetch=FetchType.LAZY,mappedBy = "venue",cascade = CascadeType.ALL)
private Set<Event> events;
//Constructors getters and setters below
Event
#Entity
public class Event
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String title;
private String description;
private String performer;
private String[] performerMedia;
private Calendar[] date;
#Transient
private double avgRating;
private int numRatings;
private int totalRating;
private String mediaRef;
#MapsId("name")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="Venue_name",referencedColumnName = "name"),
#JoinColumn(name="venue_location",referencedColumnName = "location")
})
private Venue venue;
//Constructors getters and setters below
Controller
#RequestMapping(value = "/event",method=RequestMethod.POST)
public ResponseEntity addEvent(#RequestBody Event event)
{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String name = auth.getName(); //get logged in username
Venue venue = userVenueRepository.findByEmail(name).getVenue();
event.setVenue(venue);
venue.addEvent(event);
if(eventRepository.saveAndFlush(event).equals(event)&&venueRepository.saveAndFlush(venue).equals(venue))
{
return new ResponseEntity(HttpStatus.CREATED);
}
else
{
return new ResponseEntity(HttpStatus.CONFLICT);
}
}
insert into event (id, date, description, media_ref, num_ratings, performer, performer_media, title, total_rating) values (null, ?, ?, ?, ?, ?, ?, ?, ?)
You need to set id to your Event entity. Better use #GeneratedValue annotation with AUTO, like here https://github.com/spring-projects/spring-data-jpa/blob/master/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java
or use class AbstractPersistable as parent entity.
Error says that location field of Venue entity is null which cannot be as it is primary key.
You have two options for persisting Event object.
First persist Venue [Parent] Entity and then Persist as many Event [Child] entities as you want.Set venue field in event entity. You need to save Parent entity only once.
If you want to persist both parent and child at once, you can specify cascade = CascadeType.PERSIST in Event Entity and then save child entity.
Ok so I managed to fixed this and in hindsight I shouldn't of blindly followed a tutorial, I wasn't to sure what the #MapsId did so I removed it and everything started working.If anyone could explain what #MapsId does and why it was causing problems that would be appreciated.
You can try this too.
this way you don't need to add parent entry inside child object.
Remove mappedBy form Venue entity
Then add below code inside the Venue entity before Set<Event>
#JoinColumns({
#JoinColumn(name="Venue_name",referencedColumnName = "name"),
#JoinColumn(name="venue_location",referencedColumnName = "location")
})
Remove #JoinColumns and #MapsId from Event entity
Then you don't need to write
event.setVenue(venue);
Hope it helps.
The following query throws the exception:
Query query = session.createQuery("from Associate as a order by a.username asc");
associates = query.list();
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [ca.mypkg.model.Associate#0]
If I create an entry in the database with id of 0 it works just fine. I don't really get it because I'm just trying to load all the entries in the db not just a specific one.
Similar questions I've found have been concerned with trying to load an object with a given ID I'm doing no such thing.
Associate class:
#Table(name = "user")
#XmlRootElement(name = "associate")
public class Associate implements Serializable {
private String username;
private String password;
private String firstName;
private String lastName;
private String userType;
private int id;
private String email;
private String isActive;
private Department dept;
private String lastUpdated;
private String associate_type;
// ...
#Id
#GeneratedValue
public int getId() {
return id;
}
#OneToOne
#JoinColumn(name = "dept")
public Department getDept() {
return dept;
}
From my experience this type of error message usually means it does not find joined entity by mentioned id, and not the entity requested in the query (Associate, in your case).
My guess is that Associate class contains a join entity which has primitive type primary key.
I know that there is many question about it but i can not find a good answered for my problem .
I am using Jboss as 7, Spring and Hibernate (4) as JPA 2.0 provider so i have got simple #OneToMany bi-directional relationship :
I have got super class person like that:
#MappedSuperclass
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(min = 1, max = 25)
#Pattern(regexp = "[A-Za-z ]*", message = "must contain only letters and spaces")
private String name;
public Person(String name) {
super();
this.name = name;
}
And class Member:
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = "email"))
public class Member extends Person implements Serializable
{
/** Default value included to remove warning. Remove or modify at will. **/
private static final long serialVersionUID = 1L;
#NotNull
#NotEmpty
#Email
private String email;
#NotNull
#Size(min = 10, max = 12)
#Digits(fraction = 0, integer = 12)
#Column(name = "phone_number")
private String phoneNumber;
#OneToMany(cascade=CascadeType.ALL , mappedBy="member" , fetch=FetchType.EAGER)
private List<Order> orders;
And also class Order:
#Entity
public class Order {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private float price;
#ManyToOne(optional=false)
private Member member;
private String name;
So i think that it is a good configuration, but i test this application in HSQL in memory and i have got error :
Hibernate: alter table Order drop constraint FK48E972E548C740B
2012-09-20 16:25:37 org.hibernate.tool.hbm2ddl.SchemaExport perform
ERROR: HHH000389: Unsuccessful: alter table Order drop constraint FK48E972E548C740B
2012-09-20 16:25:37 org.hibernate.tool.hbm2ddl.SchemaExport perform
ERROR: Blad skladniowy w wyrazeniu SQL "ALTER TABLE ORDER[*] DROP CONSTRAINT FK48E972E548C740B "; oczekiwano "identifier"
Syntax error in SQL statement "ALTER TABLE ORDER[*] DROP CONSTRAINT FK48E972E548C740B "; expected "identifier"; SQL statement:
alter table Order drop constraint FK48E972E548C740B [42001-165]
And also :
Syntax error in SQL statement "CREATE TABLE ORDER[*] (ID INTEGER GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR(255), PRICE FLOAT NOT NULL, MEMBER_ID BIGINT NOT NULL, PRIMARY KEY (ID)) "; expected "identifier"; SQL statement:
And my JUnit test failed i dont know what is wrong with this configuration ...
this is my simply junit :
#Test
public void testInsertWithOrder(){
Order order = new Order(20.0f, "first stuff");
Order order2 = new Order(40.0f, "secondary stuff");
List<Order> orders = new ArrayList<Order>();
orders.add(order2);
orders.add(order);
Member member = new Member("Member name", "member23#gmail.com", "2125552141", orders);
memberDao.register(member);
List<Member> members = memberDao.findAllOrderedByName();
Assert.assertNotNull(members);
Assert.assertEquals(1, members.size());
}
Change table name from 'order' to something different, like PersonOrder
In your member in Order Class, there are missing #JoinColumn annotation. Try as below.
#ManyToOne(optional=false)
#JoinColumn(name = "memberId", referencedColumnName = "id")
private Member member;
#CycDemo
I am just figure it out and in my constuctor i now have got :
#OneToMany(cascade=CascadeType.ALL , mappedBy="member" , fetch=FetchType.EAGER)
private List<UOrder> orders = new ArrayList<UOrder>();
public Member(String name, String email, String phoneNumber ,List<UOrder> orders) {
super(name);
this.orders = orders;
this.email = email;
for(UOrder o : orders){
o.setMember(this);
}
this.orders = orders;
}
Ant this is it what i need :)))
I'm trying to map an entity using Hibernate Annotations, so that when a record is created and saved (via cascade), an ID is automatically generated. With my current setup (or a few others I've tried) I get the following error:
...org.hibernate.exception.ConstraintViolationException:
could not insert: [com.gorkwobbler.shadowrun.karma.domain.AttributeScore]
...java.sql.SQLException:
Caused by: java.sql.SQLException: Cannot insert the value NULL into column 'id', table 'KARMA_DEV.dbo.Character'; column does not allow nulls. INSERT fails.
I can see the following insert statement being issued:
Hibernate: insert into character (version, alias, firstName, lastName) values (?, ?, ?, ?)
Clearly this is wrong, there is no "id" parameter.
My table schema, for now, is simply:
Character(
id uniqueidentifier, --primary key
alias varchar(max),
firstName varchar(max),
lastName varchar(max),
version int --for hibernate
)
I am using SQL Server 2008 R2, Express edition.
My annotations are split between a mapped superclass, DomainEntity, and a concrete class, KarmaCharacter:
#MappedSuperclass
public abstract class DomainEntity implements Serializable /* Needed for HOM retainUnsaved */ {
private static final long serialVersionUID = 1L;
private String id;
private Integer version;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Generated(value=GenerationTime.INSERT)
//#GeneratedValue(generator="hibernate-uuid.hex")
//#GenericGenerator(name="hibernate-uuid.hex", strategy="org.hibernate.id.UUIDHexGenerator", parameters=#Parameter(name="separator", value="-"))
#AccessType(value="field")
public String getId() {
return id;
}
#Version
#AccessType(value="field")
public Integer getVersion() {
return version;
}
}
#SuppressWarnings("serial")
#Entity
#Table(name="character")
public class KarmaCharacter extends DomainEntity {
private String alias;
private String lastName;
private String firstName;
private SortedSet<AttributeScore> attributeScores;
public KarmaCharacter() {
//default constructor
}
#Column
#AccessType(value="field")
public String getAlias() {
return alias;
}
#Column
#AccessType(value="field")
public String getFirstName() {
return firstName;
}
#Column
#AccessType(value="field")
public String getLastName() {
return lastName;
}
//...omitted some transient code and a collection property for brevity
public void setAlias(String alias) {
this.alias = alias;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
If someone could tell me the right way to generate uniqueidentifer-type ID's with hibernate in SQL Server, and get them to be saved properly, it would be much appreciated.
I can see the following insert statement being issued (...). Clearly this is wrong, there is no "id" parameter.
Indeed, when using the uniqueidentifier SQL Server type, Hibernate has to use newid(). But your current annotations are not telling it to do so. I think you need the guid generator here:
#Id
#GenericGenerator(name = "generator", strategy = "guid", parameters = {})
#GeneratedValue(generator = "generator")
public String getId() {
return id;
}
Some additional remarks:
The GUID column type is really meant to hold a GUID generated by Microsoft's algorithm, you can't use Hibernate's UUID algorithm.
You don't need the Generated annotation on an Id, just remove it.
I also wonder why you are "messing" wit AccessType, I would just remove them.
I would actually not use a GUID (see this article) but this is another story.