#ManyToOne mapping doesn't work with joined inheritance - java

I have the following database structure:
CREATE TABLE `author` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`));
CREATE TABLE `message` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(500) NOT NULL,
`text` varchar(50000) NOT NULL,
`author_id` int(10) unsigned DEFAULT NULL,
`creation_date` datetime NOT NULL,
`last_update_date` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `author_id_fk` (`author_id`),
CONSTRAINT `message_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`));
CREATE TABLE `comment` (
`id` int(10) unsigned NOT NULL,
`post_id` int(10) unsigned NOT NULL,
KEY `message_id_fk` (`id`),
KEY `post_id_fk` (`post_id`),
CONSTRAINT `comment_ibfk_1` FOREIGN KEY (`id`) REFERENCES `message` (`id`),
CONSTRAINT `comment_ibfk_2` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`));
CREATE TABLE `post` (
`id` int(10) unsigned NOT NULL,
KEY `message_id_fk` (`id`),
CONSTRAINT `post_ibfk_1` FOREIGN KEY (`id`) REFERENCES `message` (`id`) ON DELETE CASCADE);
And the following mapping with hibernate(3.5.4-Final):
#Entity
#Table(name = "author")
public class Author {
private Long id = 0L;
private String name;
private String email;
private String password;
private Set<Post> posts;
private Set<Comment> comments;
#Id
#Column(name = "id")
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "email")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Column(name = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#OneToMany(mappedBy = "author")
public Set<Post> getPosts() {
return posts;
}
public void setPosts(Set<Post> posts) {
this.posts = posts;
}
#OneToMany(mappedBy = "author")
public Set<Comment> getComments() {
return comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
}
#MappedSuperclass
#Table(name = "message")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Message implements Serializable {
private Long id;
private String title;
private String text;
private Author author;
private Date creationDate;
private Date lastUpdateDate;
#Id
#Column(name = "id")
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "title")
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Column(name = "text")
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
#ManyToOne
#JoinColumn(name = "author_id")
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
#Column(name = "creation_date")
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
#Column(name = "last_update_date")
public Date getLastUpdateDate() {
return lastUpdateDate;
}
public void setLastUpdateDate(Date lastUpdateDate) {
this.lastUpdateDate = lastUpdateDate;
}
}
#Entity
#Table(name = "comment")
#PrimaryKeyJoinColumn(name="id")
public class Comment extends Message {
private static final long serialVersionUID = 1L;
private Post post;
#ManyToOne
#JoinColumn(name = "post_id")
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
}
#Entity
#Table(name = "post")
#PrimaryKeyJoinColumn(name="id")
public class Post extends Message {
private static final long serialVersionUID = 1L;
private Set<Comment> comments;
#OneToMany(mappedBy = "post")
public Set<Comment> getComments() {
return comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
}
The main idea is that Comment and Post are inherited from Message and I would like both of them to have bidirectional relation. But when I run the following code:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
Author author = new Author();
author.setName("mike");
author.setPassword("123");
author.setEmail("mike#gmail.com");
Post post = new Post();
post.setAuthor(author);
post.setCreationDate(new Date());
post.setLastUpdateDate(new Date());
post.setText("Text");
post.setTitle("Title");
Long authorId = (Long)session.save(author);
Long postId = (Long)session.save(post);
tx.commit();
I get the following error:
ERROR JDBCExceptionReporter:101 - Unknown column 'author_id' in 'field list'
Exception in thread "main" org.hibernate.exception.SQLGrammarException: could not insert: [org.blogsample.mappingbeans.Post]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:92)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:64)
Update
As #JB Nizet mentioned before I changed #MappedSuperclass to #Entity, after that I've got another error mappedBy reference an unknown target entity property: org.blogsample.mappingbeans.Comment.author, this was solved by changing db structure(removed author_id from message table, added it to each of comment, post and created foreign keys for this column) and moving author(and getter/setter with mapping) to Comment, Post classes.

Your Message class shouldn't be annotated with #MappedSuperclass, but with #Entity. #MappedSuperclass means that entities extending this class inherit columns and associations from the super class, but these columns and associations go in the table of the subclass. author_id is not in the comment table or in the post table. It's in the message table.
Moreover #Table can only be used with an entity. Not with a mapped superclass, which is only used to inherit fields and associations, but is not mapped to its own table like an entity is.

I think your issue here is exactly the same as the one highlighted in this question.
Have a look at my answer there to see if that helps.

Related

self mapping one to many hibernate spring boot

MYSQL Schema:
CREATE TABLE `nodes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`node_id` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`type` int(255),
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `node_id_self_to_id` (`node_id`),
KEY `type_foreign_to_node_types` (`type`),
CONSTRAINT `node_id_self_to_id` FOREIGN KEY (`node_id`) REFERENCES `nodes` (`id`),
CONSTRAINT `type_foreign_to_node_types` FOREIGN KEY (`type`) REFERENCES `node_types` (`id`)
)
MY Entity Class:
#Entity
#Table(name = "nodes")
public class Nodes {
#Id
#JoinColumn(name = "node_id")
private int id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="node_id")
private Nodes nodeId;
#OneToMany(mappedBy="nodeId")
private Set<Nodes> mynodeIds = new HashSet<Nodes>();
private String name;
private Date created_at;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "type")
private Nodetypes nodetypes;
#OneToMany(mappedBy = "nodes", cascade = CascadeType.ALL)
private Set<Nodeattributes> nodeattributes;
#OneToMany(mappedBy = "nodes", cascade = CascadeType.ALL)
private Set<Products> products;
public Nodetypes getNodetypes() {
return nodetypes;
}
public void setNodetypes(Nodetypes nodetypes) {
this.nodetypes = nodetypes;
}
public Set<Products> getProducts() {
return products;
}
public void setProducts(Set<Products> products) {
this.products = products;
}
public Set<Nodeattributes> getNodeattributes() {
return nodeattributes;
}
public void setNodeattributes(Set<Nodeattributes> nodeattributes) {
this.nodeattributes = nodeattributes;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreated_at() {
return created_at;
}
public void setCreated_at(Date created_at) {
this.created_at = created_at;
}
}
and In the Junit test,
public void testCreateNodes() {
Nodes node1 = new Nodes();
node1.setId(2222);
node1.setCreated_at(new java.util.Date());
node1.setName("nodeName");
node1.setNodetypes(nodetypesRepository.findById(1111).get());
nodesRepository.save(node1);
}
I'm using Spring Boot Project. How to do a self mapping of One To Many in Hibernate. So, How can I Implement hibernate self join annotations one to many? Any help would be appreciated. I have followed this blog to develop the project, but still getting null value in my MYSQL.
https://viralpatel.net/blogs/hibernate-self-join-annotations-one-to-many-mapping/

org.hibernate.MappingException: property mapping has wrong number of columns in ENUM entity

I created database, which have a references with ENUM table and I have exception in entity of this enum.
scripts example:
CREATE TABLE status (
code VARCHAR(40),
status ENUM('not started', 'in progress', 'finished')
);
insert into status (code, status)
values (1, 'not started'),
(2, 'in progress'),
(3, 'finished');
CREATE TABLE `explorer` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`create_date` datetime DEFAULT NULL,
`query` varchar(255) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`title` varchar(255) COLLATE utf8mb4_unicode_520_ci DEFAULT NULL,
`status_id` int DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (status_id) REFERENCES status(code)
) ENGINE=MyISAM AUTO_INCREMENT=45 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
So, after creating table I did create automatically entities for this tables:
Explorer:
#Entity
#Table(name = "explorer", schema = "parsebeandeveloper", catalog = "")
public class ExplorerEntity {
private long id;
private Timestamp createDate;
private String query;
private String title;
private Integer statusId;
#Id
#Column(name = "id")
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Basic
#Column(name = "create_date")
public Timestamp getCreateDate() {
return createDate;
}
public void setCreateDate(Timestamp createDate) {
this.createDate = createDate;
}
#Basic
#Column(name = "query")
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
#Basic
#Column(name = "title")
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Basic
#Column(name = "status_id")
public Integer getStatusId() {
return statusId;
}
public void setStatusId(Integer statusId) {
this.statusId = statusId;
}
}
class, where I get exception:
Status:
#Entity
#Table(name = "status", schema = "parsebeandeveloper")
public class StatusEntity {
private Integer code;
private Object status;
#Basic
#Column(name = "code")
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
#Basic
#Column(name = "status")
public Object getStatus() {
return status;
}
public void setStatus(Object status) {
this.status = status;
}
}
That, what I get in console:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'entityManagerFactory' defined in class path
resource [org/springframework/boot/autoconfigure/orm/jpa
/HibernateJpaConfiguration.class]: Invocation of init method failed; nested > exception is javax.persistence.PersistenceException: [PersistenceUnit:
default] Unable to build Hibernate SessionFactory; nested exception is
org.hibernate.MappingException: property mapping has wrong number of
columns: com.sb.bean.parser.explorer.model.domain2.StatusEntity.status type: > object
I have think, that I did make mistake
in the creating table explorer, in string:
FOREIGN KEY (status_id) REFERENCES status(code)
or in creating class EntityStatus
How to correctly create Status entity or create references between tables in this case?
Hibernate complains that it is not able to map StatusEntity.status because it is declared Object type.
You can change it to String type which Hibernate should be able to map to database ENUM type.
You can also use Java enum for status field and have hibernate map it using #Enumerated annotation.

How do I map/create an entity class for a table which has only foreign keys in Spring Boot

I have the following tables in my database (Postgres): questions, responses and question_response.
There is a many to many relationships between questions and responses tables and I have created the entity classes for both of these relations. I now have to create an entity mapping for question_respone table which doesn't have any primary key.
I have read about using #IdClass or #EmbeddedId, however, I am not sure how do I map two foreign keys which are primary keys in two different classes using these annotations.
Note:
updating the entities after implementing the changes mentioned in the comments
Thanks!
questions.sql
CREATE TABLE questions(
id BIGSERIAL PRIMARY KEY,
question VARCHAR(255)
);
respones.sql
CREATE TABLE responses(
id BIGSERIAL PRIMARY KEY,
response VARCHAR(255)
);
question_respone.sql #
CREATE TABLE question_response(
question_id bigint REFERENCES questions ON DELETE CASCADE,
response_id bigint REFERENCES responses ON DELETE CASCADE,
PRIMARY KEY ( question_id, response_id)
);
Question.java
#Entity
#Table(name = "questions")
public class Question{
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="qid_seq")
#SequenceGenerator(name = "qid_seq", sequenceName="questions_id_seq")
#Column(name = "id")
private Long id;
#Column(name = "questionText")
private String questionText;
#OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true)
private List<QuestionResponse> responses;
public Question() {}
public Question(String questionText) {
super();
this.questionText = questionText;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getQuestionText() {
return questionText;
}
public void setQuestionText(String questionText) {
this.questionText = questionText;
}
public List<QuestionResponse> getResponses() {
return responses;
}
}
QuestionResponse.java
#Entity
#Table(name = "question_response")
public class QuestionResponse {
#Id
#ManyToOne
private Question question;
#Id
#ManyToOne
private Response response;
public QuestionResponse() {
super();
}
public QuestionResponse(Question question, Response response) {
super();
this.question= question;
this.response = response;
}
public Question getQuestion() {
return question;
}
public void setQuestion(Question question) {
this.question = question;
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
}
Response.java
#Entity
#Table(name = "responses")
public class Response {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="rid_seq")
#SequenceGenerator(name = "rid_seq", sequenceName="questions_id_seq")
#Column(name = "id")
private Long id;
#Column(name = "responseText")
private String responseText;
#OneToMany(mappedBy = "response", cascade = CascadeType.ALL, orphanRemoval = true)
private List<QuestionResponse> question;
public Response() {}
public Response(String responseText) {
super();
this.responseText = responseText;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getResponseText() {
return responseText;
}
public void setResponseText(String responseText) {
this.responseText = responseText;
}
public List<QuestionResponse> getQuestion() {
return question;
}
}
# WildFly console #
13:54:49,581 ERROR [org.springframework.boot.SpringApplication] (ServerService Thread Pool -- 86) Application run failed: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]:
Invocation of init method failed; nested exception is org.hibernate.AnnotationException:
No identifier specified for entity: com.poc.questionnarie.QuestionResponse
You can break up the many-to-many relationship into a one-to-many-to-one construct as described here:
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#associations-many-to-many-bidirectional-with-link-entity

org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: Company, for columns: [org.hibernate.mapping.Column(users)]

Hibernate is unable to determine the type for Set at the table Company. I am trying to create a foreign key of table Company through one-to-many relationship. One Company can have multiple users.
Company.java is given below:
#Entity
#Table(name = "Company")
#SuppressWarnings("serial")
public class Company implements Serializable {
#Id
#GeneratedValue
#Column(name = "companyId", length = 32 )
private int companyId;
private Set<User> users;
#Column(name = "companyName")
String companyName;
#Column(name = "typeOfBusiness")
String typeOfBusiness;
public int getCompanyId() {
return companyId;
}
public void setCompanyId(int id) {
this.companyId = id;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public String getTypeOfBusiness() {
return typeOfBusiness;
}
public void setTypeOfBusiness(String typeOfBusiness) {
this.typeOfBusiness = typeOfBusiness;
}
public Set<User> getUsers() {
return this.users;
}
#OneToMany(mappedBy="company", cascade=CascadeType.ALL, fetch=FetchType.LAZY, orphanRemoval=true)
public void setUsers(Set<User> users) {
this.users = users;
}
public Company() {
}
public Company(int companyId, Set<User> users, String companyName, String typeOfBusiness) {
this.companyId = companyId;
this.users = users;
this.companyName = companyName;
this.typeOfBusiness = typeOfBusiness;
}
}
User.java is as below:
#Entity
#Table(name = "User")
#SuppressWarnings("serial")
public class User implements Serializable {
#Id
#GeneratedValue
#Column(name = "id", length = 11 )
private Long id;
private Company company;
#Column(name = "userName")
String userName;
#Column(name = "userPassword")
String userPassword;
#Column(name = "userEmail")
String userEmail;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
public Company getCompany() {
return this.company;
}
#ManyToOne
#JoinColumn( name="companyId",insertable=false, updatable=false, nullable = false)
public void setCompany(Company company) {
this.company = company;
}
public User(){}
public User(Long id, Company company, String userName, String userPassword, String userEmail) {
super();
this.id = id;
this.company = company;
this.userName = userName;
this.userPassword = userPassword;
this.userEmail = userEmail;
}
}
And below is my database definition for mysql:
CREATE TABLE `Company` (
`companyId` bigint(32) NOT NULL AUTO_INCREMENT,
`companyName` varchar(50) NOT NULL,
`typeOfBusiness` varchar(50),
PRIMARY KEY (`companyId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `User` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userName` varchar(50) NOT NULL,
`userPassword` varchar(50) NOT NULL,
`userEmail` varchar(50),
`phoneNumber` bigint(32),
`companyId` bigint(32),
PRIMARY KEY (`id`),
FOREIGN KEY (companyId) REFERENCES Company(companyId) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
you have to mention this relation at class level also
in Company
#OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
private Set<User> users;
in User
#ManyToOne
#JoinColumn(name="company_id")
private Company company;

Select from two table from hibernate?

I have two tables in db employee and department as:
CREATE TABLE test.employee (
EMPID int(10) unsigned NOT NULL DEFAULT '1',
Name varchar(45) NOT NULL DEFAULT '1',
DEPTID int(10) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (EMPID),
KEY FK_employee_1 (DEPTID),
CONSTRAINT FK_employee_1 FOREIGN KEY (DEPTID) REFERENCES department (DEPTID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE test.department (
DEPTID int(10) unsigned NOT NULL AUTO_INCREMENT,
Name varchar(45) NOT NULL,
PRIMARY KEY (DEPTID)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
And my mapping classes are as below:
Employee2.java
#Entity
#Table(name="EMPLOYEE")
public class Employee2 {
#Id #GeneratedValue
#Column(name="EMPID")
private String ID;
#Column(name="Name")
private String Name;
#Column(name="DEPTID")
private String DepartmentID;
public Employee2(String iD, String name, String departmentID){
ID = iD;
Name = name;
DepartmentID = departmentID;
}
public Employee2(){
}
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getDepartmentID() {
return DepartmentID;
}
public void setDepartmentID(String departmentID) {
DepartmentID = departmentID;
}
#OneToOne
#JoinColumn(table = "DEPARTMENT", name = "DEPTID", referencedColumnName="DEPTID")
private Department2 ec;
public Department2 getEc() {
return ec;
}
public void setEc(Department2 ec) {
this.ec = ec;
}
}
Department2.java
#Entity
#Table(name="DEPARTMENT")
public class Department2 {
#Id #GeneratedValue
#Column(name="DEPTID")
private String ID;
#Column(name="Name")
private String Name;
public Department2(String iD, String name) {
ID = iD;
Name = name;
}
public Department2(){
}
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
}
I want to select from two tables with join as EMPLOYEE.DEPTID = DEPARTMENT.DEPTID
I dont want to write query.
Here is how I m doing it in test class
tx = session.beginTransaction();
Criteria criteria = session.createCriteria(Employee2.class, "employee").
createCriteria("employee.ec", JoinType.INNER_JOIN);
List<Equipment2> rows = criteria.list();
System.out.println(rows.size());
tx.commit();
But I m getting following exception
Failed to create sessionFactory object.org.hibernate.AnnotationException: Cannot find the expected secondary table: no DEPARTMENT available for com.cts.sm.Employee2
Exception in thread "main" java.lang.ExceptionInInitializerError
I m using Hibernate 4.2
Can you please help me as what I m missing in this.
As suggested by #jpprade
Make the following change
#ManyToOne
#JoinColumn(name ="DEPTID", updatable = false, insertable = false)
private Department2 ec;
//getter setter
Thanks
N G

Categories