I have two entity classes which have a OneToOne Bidirectional connection along with #CascadeOnDelete Tag. The DDLs generated from them do not have the on delete cascade.
#Entity(name = "Test")
#CascadeOnDelete
public class Test {
#Id
#GeneratedValue
private long id;
public long getId() {
return id;
}
public void setId(long newId) {
this.id = newId;
}
#OneToOne(cascade = { CascadeType.ALL }, orphanRemoval=true, mappedBy = "test")
#CascadeOnDelete
Test1 test1 = null;
public Test1 getTest() {
return test1;
}
public void setTest(Test1 test) {
this.test1 = test;
}
}
#Entity(name = "Test1")
#CascadeOnDelete
public class Test1 {
#Id
#GeneratedValue
private long id;
public long getId() {
return id;
}
public void setId(long newId) {
this.id = newId;
}
#OneToOne
#JoinColumn(name = "test1")
#CascadeOnDelete
Test test = null;
public Test getTest() {
return test;
}
public void setTest(Test test) {
this.test = test;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
String s;
}
Generates DDLs as
CREATE TABLE TEST (ID BIGINT NOT NULL, PRIMARY KEY (ID));
CREATE TABLE TEST1 (ID BIGINT NOT NULL, S VARCHAR(255), test1 BIGINT, PRIMARY KEY (ID));
ALTER TABLE TEST1 ADD CONSTRAINT FK_TEST1_test1 FOREIGN KEY (test1) REFERENCES TEST (ID);
CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(15), PRIMARY KEY (SEQ_NAME));
INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 0);
Why is there no ON DELETE CASCADE; at the end of Alter table? For OneToMany, it works without problem.
Similar(yet older version) question(also unanswered):
Eclipselink - #CascadeOnDelete doesn't work on #OneToOne relations
Related
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/
I am trying to map a legacy database with JPA so cannot change database structure. I have an entity TableA with a oneToMany collection tableBs where the foreign key is on TableB. The objects in this collection have a ManyToOne relationship with TableC. TableB also has a oneToOne relationship with TableD with the foreign key also on tableB. TableD has a ManyToOne relationship with TableE (tableD having foreign key) and finally TableE has a oneToOne with tableC (tableE having foreign key).
When I call save on tableA I want it to cascade any changes to the collection tableBs so I am cascading save operation. This seems to work fine but when I am adding a new TableB entity to the collection, I set a tableC object on tableB entity but after save this becomes proxy but I need this to be initialised. I have mocked up an example below..
Database tables...
CREATE TABLE TABLEA (tableAPk VARCHAR2(10) PRIMARY KEY);
CREATE TABLE TABLEC (ID NUMBER PRIMARY KEY);
CREATE TABLE TABLEE (ID NUMBER PRIMARY KEY, tableEProperty NUMBER,
CONSTRAINT FK_TABLEC2 FOREIGN KEY (tableEProperty) REFERENCES TABLEC(ID));
CREATE TABLE TABLED (ID NUMBER PRIMARY KEY, tableDProperty NUMBER,
CONSTRAINT FK_TABLEE FOREIGN KEY (tableDProperty) REFERENCES TABLEE(ID));
CREATE TABLE TABLEB (ID NUMBER PRIMARY KEY, tableBProperty VARCHAR2(10), tableBProperty2 NUMBER, tableBProperty3 NUMBER,
CONSTRAINT FK_TABLEA FOREIGN KEY (tableBProperty) REFERENCES TABLEA (tableAPk),
CONSTRAINT FK_TABLEC FOREIGN KEY (tableBProperty2) REFERENCES TABLEC (ID),
CONSTRAINT FK_TABLED FOREIGN KEY (tableBProperty3) REFERENCES TABLED (ID));
CREATE SEQUENCE TABLEA_SEQ START WITH 1;
CREATE SEQUENCE TABLEB_SEQ START WITH 1;
CREATE SEQUENCE TABLEC_SEQ START WITH 1;
CREATE SEQUENCE TABLED_SEQ START WITH 1;
CREATE SEQUENCE TABLEE_SEQ START WITH 1;
Java code:
#Entity
public class TableA {
#Id
private String tableAPk;
#OneToMany(mappedBy="tableBProperty", fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval=true)
private List<TableB> tableBs = new ArrayList<TableB>();
public String getTableAPk() {
return tableAPk;
}
public void setTableAPk(String tableAPk) {
this.tableAPk = tableAPk;
}
public List<TableB> getTableBs() {
return tableBs;
}
public void setTableBs(List<TableB> tableBs) {
this.tableBs = tableBs;
}
}
#Entity
public class TableB {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLEB_SEQ")
#SequenceGenerator(sequenceName = "TABLEB_SEQ", allocationSize = 1, name = "TABLEB_SEQ")
private Integer id;
private String tableBProperty;
#ManyToOne
#JoinColumn(name = "tableBProperty2")
private TableC tableC;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="tableBProperty3")
private TableD tableD;
public TableB() {}
public TableB(String tableBProperty, TableC tableC, TableD tableD) {
this.tableBProperty = tableBProperty;
this.tableC = tableC;
this.tableD = tableD;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTableBProperty() {
return tableBProperty;
}
public void setTableBProperty(String tableBProperty) {
this.tableBProperty = tableBProperty;
}
public TableC getTableC() {
return tableC;
}
public void setTableC(TableC tableC) {
this.tableC = tableC;
}
public TableD getTableD() {
return tableD;
}
public void setTableD(TableD tableD) {
this.tableD = tableD;
}
}
#Entity
public class TableC {
#Id private Integer id;
#OneToOne(mappedBy="tableC")
private TableE tableE;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public TableE getTableE() {
return tableE;
}
public void setTableE(TableE tableE) {
this.tableE = tableE;
}
}
#Entity
public class TableD {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="TABLED_SEQ")
#SequenceGenerator(sequenceName = "TABLED_SEQ", allocationSize = 1, name = "TABLED_SEQ")
private Integer id;
#ManyToOne(cascade={CascadeType.PERSIST})
#JoinColumn(name="tableDProperty")
private TableE tableE;
public TableD() {}
public TableD(TableE tableE) {
this.tableE = tableE;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
#Entity
public class TableE {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="TABLEE_SEQ")
#SequenceGenerator(sequenceName = "TABLEE_SEQ", allocationSize = 1, name = "TABLEE_SEQ")
private Integer id;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "tableEProperty")
private TableC tableC;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public TableC getTableC() {
return tableC;
}
public void setTableC(TableC tableC) {
this.tableC = tableC;
}
}
public interface TableARepository extends JpaRepository<TableA, String>{
}
public interface TableCRepository extends JpaRepository<TableC, Integer> {
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class TableARepositoryTest {
private static final Integer TEST_ID = -1;
private static final String TEST_ID_STRING = "TEST1";
#Autowired protected DataSource ds;
#Autowired private TableARepository repoA;
#Autowired private TableCRepository repoC;
protected JdbcTemplate jdbcTemplate;
#Before
public void setUp() throws Exception{
jdbcTemplate = new JdbcTemplate(ds);
String insertASql = "insert into TableA (tableAPk) values (?)";
jdbcTemplate.update(insertASql, new Object[]{TEST_ID_STRING});
String insertCSql = "insert into TableC (id) values (?)";
jdbcTemplate.update(insertCSql, new Object[]{TEST_ID});
String insertESql = "insert into TableE (id, tableEProperty) values (?, ?)";
jdbcTemplate.update(insertESql, new Object[]{TEST_ID, TEST_ID});
}
#After
public void tearDown() throws Exception{
String deleteBSql = "delete from TableB where tableBProperty = ?";
jdbcTemplate.update(deleteBSql, new Object[]{TEST_ID_STRING});
String deleteDSql = "delete from TableD where tableDProperty = ?";
jdbcTemplate.update(deleteDSql, new Object[]{TEST_ID});
String deleteESql = "delete from TableE where ID = ?";
jdbcTemplate.update(deleteESql, new Object[]{TEST_ID});
String deleteASql = "delete from TableA where tableAPk = ?";
jdbcTemplate.update(deleteASql, new Object[]{TEST_ID_STRING});
String deleteCSql = "delete from TableC where ID = ?";
jdbcTemplate.update(deleteCSql, new Object[]{TEST_ID});
}
#Test
public void test() {
TableA tableA = repoA.findById(TEST_ID_STRING).get();
TableC tableC = repoC.findById(TEST_ID).get();
tableA.getTableBs().add(new TableB(TEST_ID_STRING, tableC, new TableD(tableC.getTableE())));
TableA updatedTableA = null;
try {
updatedTableA = repoA.save(tableA);
} catch(Exception e) {
fail("test:"+e.getMessage());
}
assertNotNull(updatedTableA);
assertTrue(Hibernate.isInitialized(updatedTableA.getTableBs().get(0).getTableC()));
}
}
This test passes but if you inspect the returned object, it is a proxy with value TableC_$$_jvst.... My app will then fall over when it tries to serialise this object (which I need).
Wrap your entity in question with :
public static <T> T unproxy(T entity){
Hibernate.initialize(entity)
if(entity instanceof HibernateProxy){
entity = (T)((HibernateProxy)entity).getHibernateLazyInitializer().getImplementation();
} else {
entity = (T)entity
}
return entity;
}
It'll return you the unproxied Object.
I need help on hibernate mapping for a bean property refers to multiple classes.
In my application we are implementing permissions. These permission are not specific to certain user it may based on groups(contains list of users) and roles. So, Permissions will apply to users, roles and groups.
Following are ddl and entity classes. Please review and help me.
DDL:
--stores the application users
CREATE TABLE users (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_users_name UNIQUE (name)
);
--stores the application groups
CREATE TABLE groups (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_groups_name UNIQUE (name)
);
--stores the application roles
CREATE TABLE roles (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_roles_name UNIQUE (name)
);
--stores the application object types
CREATE TABLE app_object_types (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_app_object_types_name UNIQUE (name)
);
INSERT INTO app_object_types (name) VALUES ('USERS');
INSERT INTO app_object_types (name) VALUES ('GROUPS');
INSERT INTO app_object_types (name) VALUES ('ROLES');
CREATE TABLE app_permissions (
id serial PRIMARY KEY,
object_type_id integer REFERENCES app_object_types(id), -- To represent the object type
object_id integer, -- Objecct_id refers users -> id, groups -> id, roles - id
permission_name text,
CONSTRAINT uk_permissions UNIQUE (object_type_id, object_id, permission_name)
);
Entity Classes:
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "groups")
public class Groups {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "roles")
public class Roles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "app_object_types")
public class AppObjectTypes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "app_permissions")
public class AppPermissions {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
private String permissionName;
#ManyToOne
private AppObjectTypes appObjectTypes;
private int objectId;
private Class<?> dependentObject;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
public AppObjectTypes getAppObjectTypes() {
return appObjectTypes;
}
public void setAppObjectTypes(AppObjectTypes appObjectTypes) {
this.appObjectTypes = appObjectTypes;
}
public int getObjectId() {
return objectId;
}
public void setObjectId(int objectId) {
this.objectId = objectId;
}
public Class<?> getDependentObject() {
return dependentObject;
}
public void setDependentObject(Class<?> dependentObject) {
this.dependentObject = dependentObject;
}
}
I want to map user (or) group (or) role bean object to AppPermissions -> dependentObject using hibernate. I don't know it is possible or not please help me.
I would suggest you consider the use of #Inheritance here on your AppPermission entity in order to specialize each subclass based on the dependent object types.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "OBJECT_TYPE")
public class AppPermission {
#Id
#GeneratedValue
private Long permissionId;
private String name;
#Column(name = "OBJECT_TYPE", insertable = false, updatable = false)
private String objectType;
}
#Entity
#DiscriminatorValue("USER")
public class UserAppPermission extends AppPermission {
#ManyToOne(optional = false)
private User user;
}
#Entity
#DiscriminatorValue("ROLE")
public class RoleAppPermission extends AppPermission {
#ManyToOne(optional = false)
private Role role;
}
#Entity
#DiscriminatorValue("GROUP")
public class GroupAppPermission extends AppPermission {
#ManyToOne(optional = false)
private Group group;
}
The first difference here with these mappings from yours is that your AppPermission table will be constructed differently from your current schema and would look like the following (note 4 tables):
Table: AppPermission
id NOT NULL IDENTITY(1,1)
name VARCHAR(255)
OBJECT_TYPE VARCHAR(31)
Table: UserAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
user_id NOT NULL BIGINT (FK -> User)
Table: RoleAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
role_id NOT NULL BIGINT (FK -> Role)
Table: GroupAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
group_id NOT NULL BIGINT (FK -> Group)
The whole point of a database is to help us maintain referential integrity. That's why when a table depends on a row from another table, the dependent table rows that relate to the row you wish to remove should be removed first to avoid constraint violations. This is precisely why I have split the relations into separate tables and here I've defined each relation as "optional=false" so that basically it represents a join-table.
Another additional benefit is that if your AppPermission has attributes you need to store specific to the type of dependent object, you can freely add those attributes to the subclass and those attributes are stored separately in that specific subclass's table.
This setup also eliminates your AppObjectType table because that is now driven as part of Hibernate's discriminator pattern. Be aware that if you have other "object-types" you'll need to introduce their specific implementations too with this setup.
Lastly, I exposed (which you don't have to) the OBJECT_TYPE as an non-insertable and non-updatable field because Hibernate manages that for you. But I've exposed it allowing you to make polymorphic queries and determine the object type of the resulting object without having to perform instanceof checks if you wish.
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
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.