I've got an issue with Hibernate. What I have to do is to retrieve a collection of Team when I select a Deliverable. Here's the Deliverable class:
#Entity(name="Deliverable")
{"MilestoneID", "TeamID"})})
#Table(name="Deliverable")
public class Deliverable implements Serializable, Comparable<Deliverable> {
private static final long serialVersionUID = 2138806103760654922L;
#Id
#Column(name="DeliverableID", nullable=false)
#GeneratedValue(strategy=GenerationType.AUTO)
private int deliverableID;
#ManyToOne(optional=false)
#JoinColumn(name="MilestoneID", nullable=false)
private Milestone milestone;
#ManyToOne(optional=false)
#JoinColumn(name="TeamID", nullable=false)
private Team team;
#Column(name="Score", nullable=false)
private int score;
#OneToMany(mappedBy="deliverable")
private Set<Version> versions;
#OneToMany(mappedBy="visibleDeliverables")
private Collection<Team> viewers;
I haven't copied getters and setters for space reasons. This is the query I want to execute:
#NamedQuery(
name="Deliverable.getDeliverableById",
query="SELECT d FROM Deliverable AS d LEFT JOIN FETCH d.viewers AS v WHERE d.deliverableID = :id"
)
And here's the exception I get:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'viewers1_.DeliverableID' in 'field list'
It's getting frustrating =(
EDIT: here's the sql to create the Deliverable table
CREATE TABLE IF NOT EXISTS `Deliverable` (
`DeliverableID` INT NOT NULL AUTO_INCREMENT ,
`MilestoneID` INT NOT NULL ,
`TeamID` INT NOT NULL ,
`Score` INT NOT NULL DEFAULT 1 ,
PRIMARY KEY (`DeliverableID`) ,
INDEX `fk_Delivarable_Milestone` (`MilestoneID` ASC) ,
INDEX `fk_Delivarable_Team` (`TeamID` ASC) ,
UNIQUE INDEX `MilestoneID_TeamID_UNIQUE` (`MilestoneID` ASC, `TeamID` ASC) ,
CONSTRAINT `fk_Delivarable_Milestone`
FOREIGN KEY (`MilestoneID` )
REFERENCES `Milestone` (`MilestoneID` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_Delivarable_Team`
FOREIGN KEY (`TeamID` )
REFERENCES `Team` (`TeamID` )
ON DELETE CASCADE
ON UPDATE CASCADE)
EDIT: and here's the Team entity
public class Team implements Serializable {
private static final long serialVersionUID = -6434099499828214268L;
#Id
#Column(name="TeamID", nullable=false)
#GeneratedValue(strategy=GenerationType.AUTO)
private int teamID;
#ManyToOne(optional=false)
#JoinColumn(name="ProjectID", nullable=false)
private Project project;
#ManyToOne(optional=false)
#JoinColumn(name="Founder", nullable=false)
private Student founder;
#Column(name="Name", nullable=false, length=100)
private String name;
#ManyToMany
#JoinTable(name="Student_Invite", joinColumns={#JoinColumn(name="TeamID")},
inverseJoinColumns={#JoinColumn(name="StudentID")})
private Set<Student> invitedStudents = new HashSet<Student>();
#ManyToMany
#JoinTable(name="Student_Member", joinColumns={#JoinColumn(name="TeamID")},
inverseJoinColumns={#JoinColumn(name="StudentID")})
private Set<Student> members = new HashSet<Student>();
#ManyToMany
#JoinTable(name="Deliverable_View", joinColumns={#JoinColumn(name="ViewerTeamID")},
inverseJoinColumns={#JoinColumn(name="DeliverableID")})
private Set<Deliverable> visibleDeliverables = new HashSet<Deliverable>();
#OneToMany(mappedBy="team")
private Set<Deliverable> deliverables = new HashSet<Deliverable>();
#Column(name="Frozen", nullable=false)
private boolean frozen;
EDIT: Deliverable_View
CREATE TABLE IF NOT EXISTS `Deliverable_View` (
`ViewerTeamID` INT NOT NULL ,
`ViewedTeamID` INT NOT NULL ,
`DeliverableID` INT NOT NULL ,
PRIMARY KEY (`ViewerTeamID`, `ViewedTeamID`, `DeliverableID`) ,
INDEX `fk_ViewerTeamID` (`ViewerTeamID` ASC) ,
INDEX `fk_ViewedTeamID` (`ViewedTeamID` ASC) ,
INDEX `fk_DeliverableView_Delivarable` (`DeliverableID` ASC) ,
CONSTRAINT `fk_ViewerTeamID`
FOREIGN KEY (`ViewerTeamID` )
REFERENCES `Team` (`TeamID` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_ViewedTeamID`
FOREIGN KEY (`ViewedTeamID` )
REFERENCES `Team` (`TeamID` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_DeliverableView_Delivarable1`
FOREIGN KEY (`DeliverableID` )
REFERENCES `Deliverable` (`DeliverableID` )
ON DELETE CASCADE
ON UPDATE CASCADE)
Related
I am a bit disconcerted with this and I am asking for help just in order to understand what is happening.
So, I have this table (DDL):
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(45) NOT NULL,
`lastname` varchar(45) NOT NULL,
`email` varchar(60) NOT NULL,
`password` varchar(100) NOT NULL,
`enabled` tinyint(1) NOT NULL,
`created_at` datetime DEFAULT NULL,
`image_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email_UNIQUE` (`email`),
KEY `FK17herqt2to4hyl5q5r5ogbxk9` (`image_id`),
CONSTRAINT `FK17herqt2to4hyl5q5r5ogbxk9` FOREIGN KEY (`image_id`) REFERENCES `images` (`id`),
CONSTRAINT `fk_users_images1` FOREIGN KEY (`image_id`) REFERENCES `images` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
And my entity:
#Entity
#Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 6304869652591140818L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#Column(name = "firstname")
private String firstname;
#Column(name = "lastname")
private String lastname;
#Column(name = "email")
private String email;
#Column(name = "password")
#Transient
private String password;
#Column(name = "enabled")
private int enabled;
#Column(name = "created_at")
private LocalDateTime createdAt;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "users_has_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
#MapsId
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Image image;
// Setters and Getters
}
I am trying to persist a new User with this code:
#PersistenceContext
private EntityManager em;
#Transactional
public void createUser(NewUserDto newUser) {
User user = new User();
user.setFirstname(newUser.getFirstname());
user.setLastname(newUser.getLastname());
user.setEmail(newUser.getEmail());
user.setEnabled(ENABLED);
user.setPassword(bCryptPasswordEncoder.encode(newUser.getPassword()));
Image userImage = imageService.getImageById(1L);
user.setProfileImage(userImage);
Set<Role> roles = new HashSet<Role>();
Role role = roleRepository.findByName(newUser.getRole());
roles.add(role);
user.setRoles(roles);
if (user.getId() == null) {
em.persist(user);
em.flush();
} else {
em.merge(user);
}
}
The thing is that when I debug I see that always generates id=1 for the new user.
But in the database, I see all correct.
And when i search for all users i get the following:
public List<User> getAll() {
return userRepository.findAll();
}
The same JPDA ObjectReference.
Any help will be welcomed.
Thanks.
(Apologies for not having enough reputation to leave a comment)
I do not understand your question clearly. Anyway i will try to explain based on what I think is your problem.
By default, the starting value for AUTO_INCREMENT is 1, and it will
increment by 1 for each new record. So that behavior is expected.
If you want it to start with a value that is not 1, then you must use
a GenerationType.SEQUENCE with appropriate changes.
Image1: You have mentioned that the values are getting entered into
the database correctly and this shows that the Id values are being
generated correctly
Image2: users is a Javascript array. Index values of javascript
arrays always start from zero. That is just the index value and NOT
the id value. Similarly, the id mentioned on the right is the
javascript's internal object id and not the actual Id of your user.
To see the real id of the user, expand the user at position 0.
If this does not answer your questions, kindly elaborate your question and what exactly is the error or issue.
The problem stems from a misunderstanding where Hibernate's fallback for GenerationType.AUTO is GenerationType.SEQUENCE, while what most people want is GenerationType.IDENTITY, so you can use the id the database assigns you. simply change the strategy to IDENTITY and your problem is solved.
I'm newb to JPA. I'm having trouble with defining OneToOne bidirectional flow on my classes.
UserInfo.java:
#Entity
#Table(name="UserInfo")
public class UserInfo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idUserInfo;
private String firstName;
private String lastName;
#OneToOne(mappedBy="UserInfo")
private LoginInfo loginInfo;
LoginInfo.java:
#Entity
#Table(name="LoginInfo")
public class LoginInfo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idLoginInfo;
#Column(name="emailId")
private String emailId;
//bidirectional one to one association to UserInfo
#OneToOne
#JoinColumn(name="emailId", referencedColumnName="loginInfo")
private UserInfo userInfo;
private String sessionId;
private String password;
Here's sql to create those tables:
CREATE TABLE `LoginInfo` (
`emailId` varchar(45) NOT NULL,
`idLoginInfo` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`idLoginInfo`),
UNIQUE KEY `emailId_UNIQUE` (`emailId`),
UNIQUE KEY `id_UNIQUE` (`idLoginInfo`)
);
DROP TABLE IF EXISTS `UserInfo`;
CREATE TABLE `UserInfo` (
`idUserInfo` int(11) NOT NULL AUTO_INCREMENT,
`firstName` varchar(45) NOT NULL,
`lastName` varchar(45) NOT NULL,
`loginInfo` varchar(45) NOT NULL,
PRIMARY KEY (`idUserInfo`),
UNIQUE KEY `idUserInfo_UNIQUE` (`idUserInfo`),
UNIQUE KEY `loginInfo_UNIQUE` (`loginInfo`),
CONSTRAINT `loginInfo` FOREIGN KEY (`loginInfo`) REFERENCES `LoginInfo` (`emailId`) ON DELETE CASCADE ON UPDATE CASCADE
);
When I start the Tomcat server, I'm getting the follow exception on ContextInitialization.
Caused by: org.hibernate.MappingException: Unable to find column with logical name: loginInfo in org.hibernate.mapping.Table(UserInfo) and its related supertables and secondary tables
But the column do exist on the UserInfo table. Can somebody please help me if the mapping is right?
Thanks
Invalid association : #JoinColumn(name="emailId", referencedColumnName="loginInfo")
#Entity
#Table(name="UserInfo")
public class UserInfo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idUserInfo;
private String firstName;
private String lastName;
#OneToOne(mappedBy = "userInfo", cascade = CascadeType.ALL)
private LoginInfo loginInfo;
#Entity
#Table(name="LoginInfo")
public class LoginInfo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idLoginInfo;
#Column(name="emailId")
private String emailId;
//bidirectional one to one association to UserInfo
#OneToOne
#PrimaryKeyJoinColumn
private UserInfo userInfo;
private String sessionId;
private String password;
After reading about OneToOne mapping, annotations and some trial & error, I was able to use the LoginInfo and UserInfo tables successfully. In my question above, it has some invalid mappings. The requirement was that UserInfo be the owner entity and LoginInfo as the child entity. So as suggested by ashokhein, I used PrimaryKeyJoinColumn annotation. This is how my tables look like now.
CREATE TABLE `login_info` (
`user_info_id` bigint(100) NOT NULL,
`email_id` varchar(45) NOT NULL,
PRIMARY KEY (`user_info_id`),
UNIQUE KEY `email_id_UNIQUE` (`email_id`),
UNIQUE KEY `user_info_id_UNIQUE` (`user_info_id`),
CONSTRAINT `FK_login_info_user_info` FOREIGN KEY (`user_info_id`) REFERENCES `user_info` (`user_info_id`) ON DELETE CASCADE ON UPDATE CASCADE);
CREATE TABLE `user_info` (
`user_info_id` bigint(100) NOT NULL AUTO_INCREMENT,
`first_name` varchar(50) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`user_info_id`),
UNIQUE KEY `user_info_id_UNIQUE` (`user_info_id`));
This is how my classes look like:
UserInfo:
#Entity
#Table(name = "user_info")
public class UserInfo {
#Id
#Column(name = "user_info_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer userId;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private LoginInfo loginInfo;
//Getters and setters
LoginInfo:
#Entity
#Table(name = "login_info")
public class LoginInfo {
#Id
#Column(name = "user_info_id")
#GeneratedValue(generator = "generator")
#GenericGenerator(name = "generator", strategy = "foreign", parameters = #Parameter(name = "property", value = "userInfo"))
private Integer id;
#OneToOne
#PrimaryKeyJoinColumn
private UserInfo userInfo;
#Column(name = "email_id")
private String emailId;
//Getters and setters
And this is how I create and save the entities to the table:
UserInfo userInfo = new UserInfo();
userInfo.setFirstName(registerUserRequest.getFirstName());
userInfo.setLastName(registerUserRequest.getLastName());
LoginInfo loginInfo = userInfo.getLoginInfo();
if(loginInfo == null) {
loginInfo = new LoginInfo();
}
loginInfo.setEmailId(registerUserRequest.getEmailId());
loginInfo.setUserInfo(userInfo);
userInfo.setLoginInfo(loginInfo);
if(userService.create(userInfo)) {
logger.debug("User created successfully");
} else {
throw new UserAlreadyExistException();
}
During the course of getting to this solution, I encountered bunch of hibernate exceptions and issues. Here's some(if it helps someone)
Error: identifier of an instance of was altered from 14 to 14
Solution: The primary key on the Entity classes where different types initially. The column user_info_id was declared as Integer in UserInfo and long in LoginInfo.
Error: attempted to assign id from null one-to-one property
Solution: While creating the UserInfo object, I did "userInfo.setLoginInfo(loginInfo)" but did not set "loginInfo.setUserInfo(userInfo)". After fixing that, it was fine.
Note: If you see a better way to do the same, please do comment here and let me know. Thanks in advance.
I have two tables - bill & billSimpleentry and two corresponding Entity classes Bill & BillSimpleEntry.
Bill and BillSimpleentry have a one-one relationship. Each bill has one billsimpleentry. So billsimplementry.billId has the same corresponding value of bill.id.
SQL structure:
CREATE TABLE `bill` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(100) DEFAULT NULL,
.....
.....
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
KEY `fk_bill_groups1_idx` (`groupId`),
KEY `fk_bill_user1_idx` (`billPayerId`),
CONSTRAINT `fk_bill_groups` FOREIGN KEY (`groupId`) REFERENCES `groups` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_bill_user` FOREIGN KEY (`billPayerId`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = #saved_cs_client */;
CREATE TABLE `billsimpleentry` (
`itemTitle` varchar(200) DEFAULT NULL,
`itemDescription` text,
`billId` bigint(20) NOT NULL,
PRIMARY KEY (`billId`),
KEY `fk_bill_idx` (`billId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = #saved_cs_client */;
So when a new 'bill' object is persisted, it should also create a billsimpleentry row in the database.
save(Bill newBill){
em.persist(newBill);
}
Bill class structure:
#Entity
#Table(name = "bill")
public class Bill implements GenericObject {
private static final long serialVersionUID = -5660869020353250221L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
....
private Long groupId;
private BigDecimal billTotal;
#OneToOne(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn
private BillSimpleEntry billSimpleEntry;
... getters & setters...
}
BillSimpleEntry:
#Entity
#Table(name="billsimpleentry")
public class BillSimpleEntry implements GenericObject{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long billId;
#Column(columnDefinition="TEXT")
private String itemDescription;
#OneToMany(cascade=CascadeType.ALL,mappedBy="billSimpleEntryId",fetch = FetchType.EAGER)
private List<SimpleUserIdAndLiableCost> simpleUserIdAndLiableCost = new ArrayList<SimpleUserIdAndLiableCost>();
... getters & setters...
}
Here is the newBill obj data that is attempted to be persisted
{
"id":null,
"title":"",
"billDate":null,
"billPayerId":6,
"notes":null,
"billCreaterId":null,
"groupId":3,
"billTotal":null,
"billSimpleEntry":{
"billId":null,
"itemDescription":null,
"simpleUserIdAndLiableCost":[
{
"userId":6,
"liableCost":"50",
"id":null,
"billSimpleEntryId":null,
"user":{
"id":null,
"fName":"doe",
"lName":"doe"
},
"isActive":true
},
{
"userId":7,
"liableCost":"50",
"id":null,
"billSimpleEntryId":null,
"user":{
"id":null,
"fName":"doe",
"lName":"doe"
},
"isActive":true
},
{
"userId":8,
"liableCost":"50",
"id":null,
"billSimpleEntryId":null,
"user":{
"id":null,
"fName":"doe",
"lName":"doe"
},
"isActive":true
}
],
"itemDescriptionId":2
},
"billItemEntry":[
],
"userId":null
}
But the problem is that em.persist(Bill) fails because billsimpleentry.billId value needs to be populated to the same value as of bill.id. How should I fix this problem? It appears like I need to update my table structure or the table auto id generation strategy. Any insights would be appreciated.
Sorry, but I need more clarification. I don't know how you create that JSON, but in them is this "billId":null. You need to set the parent ID. So, if that is JSON, process it before persist and attach parent ID to it. Save or persist should not attach parent id to childs automatically.
This is what my SQL tables look like:
CREATE TABLE IF NOT EXISTS `test`.`Families` (
`id` INT NOT NULL AUTO_INCREMENT,
`mother_id` INT DEFAULT NULL ,
`father_id` INT DEFAULT NULL ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `test`.`Parents` (
`id` INT NOT NULL AUTO_INCREMENT,
`first_name` VARCHAR(50) DEFAULT NULL,
`last_name` VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
And this is what my family entity looks like:
#Entity
#Table(name="Families")
public class Family implements Serializable {
#Id
#Column(name="id")
private String id;
#Column(name="mother_id")
private int mother;
#Column(name="father_id")
private int father;
}
Which is great and all, but I would really LOVE if I could do something like this (note I also have a Parent entity already defined):
#Entity
#Table(name="Families")
public class Family implements Serializable {
#Id
#Column(name="id")
private String id;
#OneToOne
#Column(name="mother_id")
private Parent mother;
#OneToOne
#Column(name="father_id")
private Parent father;
}
How could I go about making this happen?
Actually, Hibernate does everything for you.
You don't need to annotate columns with #Column which already have #OneToOne or other association annotations
If you want to use other than default foreign key(by default name consists of field + _id), you should use #JoinColumn annotation
#Entity
#Table(name="Families")
public class Family implements Serializable {
#Id
#Column(name="id")
private String id;
#OneToOne
#JoinColumn(name = "mother_idd")
private Parent mother;
#OneToOne
#JoinColumn(name = "father_idd")
private Parent father;
}
I am having problems performing a cascade persist operation on a parent entity. When the child entity is persisted, the reference (generated id) to the parent entity is null. How would I get this to persist correctly?
Entities:
#Entity
public class Contact {
#Id #GeneratedValue(strategy=GenerationType.TABLE, generator="contact_gen")
#TableGenerator(name="contact_gen",
table="id_gen", pkColumnName="gen_name",
valueColumnName="gen_val", pkColumnValue="cont_gen")
#Column(name="contact_id")
private Long id;
#Column(name="name")
private String name;
#OneToMany(mappedBy="contact", cascade=CascadeType.PERSIST)
private List<Address> addresses = new ArrayList<Address>();
public void addAddress(Address address) {
addresses.add(address);
}
...
}
#Entity
public class Address {
#Id #GeneratedValue(strategy=GenerationType.TABLE, generator="address_gen")
#TableGenerator(name="address_gen",
table="id_gen", pkColumnName="gen_name",
valueColumnName="gen_val", pkColumnValue="addr_gen")
#Column(name="address_id")
private Long id;
#Column(name="full_address")
private String fullAddress;
#ManyToOne
#JoinColumn(name="contact_id")
private Contact contact;
...
}
Service:
#Stateless
public class ContactService {
#PersistenceContext
private EntityManager em;
public void createContact() {
Contact contact = new Contact();
contact.setName("Michael Scott");
contact.addAddress(new Address("1725 Slough Avenue");
em.persist(contact);
}
}
MySQL Tables & Inserts:
CREATE TABLE `contact` (
`contact_id` int(11) NOT NULL,
`name` varchar(45) NOT NULL
PRIMARY KEY (`contact_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `address` (
`address_id` int(11) NOT NULL,
`full_address` varchar(100) NOT NULL,
`contact_id` int(11) NOT NULL,
PRIMARY KEY (`address_id`),
KEY `FK_ADDRESS_contact_id` (`contact_id`),
CONSTRAINT `FK_ADDRESS_contact_id` FOREIGN KEY (`contact_id`) REFERENCES `contact` (`contact_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE id_gen (
gen_name VARCHAR(80),
gen_val INT,
PRIMARY KEY (gen_name)
);
INSERT INTO id_gen (gen_name, gen_val) VALUES ('cont_gen', 0);
INSERT INTO id_gen (gen_name, gen_val) VALUES ('addr_gen', 0);
Sadly, you're not showing the content of addAddress. Since your association is bidirectional, are you setting "both sides of the link" in this method? Something like this:
#Entity
public class Contact {
...
#OneToMany(mappedBy="contact", cascade=CascadeType.PERSIST)
private List<Address> addresses = new ArrayList<Address>();
public void addToAddresses(Address address) {
address.setContact(this);
this.addresses.add(address);
}
}