I have two entities - user and accessgroup. When getting user entity from MySQL database using RowMapper in JdbcTemplate I have NullPointerException. When not using setter for accessgroup in UserRowMapper I dont have NPE but have null in AccessGroup accessGroup.
Table
CREATE TABLE `users` (
`USER_ID` int(11) NOT NULL AUTO_INCREMENT,
`USER_EMAIL` varchar(255) DEFAULT NULL,
`USER_NAME` varchar(255) DEFAULT NULL,
`USER_PWD` varchar(255) DEFAULT NULL,
`ACCESSGROUP_GROUP_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`USER_ID`),
KEY `FK_users_ACCESSGROUP_GROUP_ID` (`ACCESSGROUP_GROUP_ID`),
CONSTRAINT `FK_users_ACCESSGROUP_GROUP_ID` FOREIGN KEY (`ACCESSGROUP_GROUP_ID`) REFERENCES `access_group` (`GROUP_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
CREATE TABLE `access_group` (
`GROUP_ID` int(11) NOT NULL AUTO_INCREMENT,
`GROUP_NAME` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`GROUP_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
Entities
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "USER_ID")
private Integer userId;
#Column(name = "USER_EMAIL")
private String userEmail;
#Column(name = "USER_NAME")
private String userName;
#Column(name = "USER_PWD")
private String userPwd;
#JoinColumn(name = "ACCESSGROUP_GROUP_ID", referencedColumnName = "GROUP_ID")
#ManyToOne
private AccessGroup accessGroup;
#Entity
#Table(name = "access_group")
public class AccessGroup implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "GROUP_ID")
private Integer groupId;
#Basic(optional = false)
#Column(name = "GROUP_NAME")
private String groupName;
#OneToMany(mappedBy = "accessGroup")
private Set users;
Dao
#Repository("userDao")
public class UserDaoImpl implements IUserDao {
#Autowired
private JdbcTemplate jdbcTemplate;
#Value("${sql.user.get.email.pwd}")
private String getByEmailAndPwd;
//sql.user.get.email.pwd=SELECT * FROM users WHERE user_email = ? AND user_pwd = ?
#Transactional
#Override
public User getUserByEmailAndPwd(String email, String password) {
return jdbcTemplate.queryForObject(getByEmailAndPwd, new Object[]{email, password}, new UserRowMapper());
}
#Repository("accessGroupDao")
public class AccessGroupDaoImpl implements IAccessGroupDao {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Value("${sql.accessgroup.get.id}")
private String getAccessGroupById;
//sql.accessgroup.get.id=SELECT * FROM access_group WHERE GROUP_ID = ?
#Transactional
#Override
public AccessGroup getGroupById(int id) {
return jdbcTemplate.queryForObject(getAccessGroupById, new Object[]{id}, new AccessGroupRowMapper());
}
RowMappers
#Component
public class UserRowMapper implements RowMapper {
private AccessGroupService accessGroupService;
#Autowired
public void setAccessGroupService(AccessGroupService accessGroupService) {
this.accessGroupService = accessGroupService;
}
#Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setId(resultSet.getInt("USER_ID"));
user.setEmail(resultSet.getString("USER_EMAIL"));
user.setName(resultSet.getString("USER_NAME"));
user.setpwd(resultSet.getString("USER_PWD"));
//when adding here user.setAccessGroup(accessGroupService.getGroupById(resultSet.getInt("ACCESSGROUP_GROUP_ID"))); I have NPE
return user;
}
public class AccessGroupRowMapper implements RowMapper {
#Override
public AccessGroup mapRow(ResultSet resultSet, int i) throws SQLException {
AccessGroup accessGroup = new AccessGroup();
accessGroup.setId(resultSet.getInt("GROUP_ID"));
accessGroup.setName(resultSet.getString("GROUP_NAME"));
return accessGroup;
}
}
1) Add the following property to your #Entity class 'User' & generate the getter & setter methods:
#Transient
private Integer userIdTransientField;
#Transient means the property will not be stored in the database
2) Modify RowMapper class:
Replace this line with the following :
user.setId(resultSet.getInt("USER_ID")); -> user.setUserIdTransientField(resultSet.getInt("USER_ID"));
3) When you refer to a 'User' object and you want to do a Update/Delete for jdbcTemplate or simply want to get the Id value then use the getUserIdTransientField method.
Related
Can someone help me with a simple explanation of how to use #Embeddable ?
I have this situation below!
One company, has several employees, and my employee table has 2 ID fields (register_number and name)
Is this the right approach?
#Entity
#Table(name = "COMPANY")
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private Employee employeeId;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
#Column(name = "ID", unique = true, nullable = false, precision = 38)
private Long id;
#Column(name = "NAME", nullable = false, length = 50)
private String name;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToMany(fetch = FetchType.LAZY, mappedBy = "registerNumber")
private Set<Employee> employees;
}
//
#Embeddable
#Entity
#Table(name = "EMPLOYEE")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "REGISTER_NUMBER", nullable = false, length = 100)
private String registerNumber;
#Column(name = "NAME", nullable = false, length = 50)
private String name;
#Column(name = "EMAIL", nullable = false, length = 50)
private String email;
}
I think you are looking a way to support composite key using hibernate. You should move those fields to a new class and annotate that class with #Embeddable.
For classes mentioned in the question, create a new Embeddable class EmployeeIdClass with the fields (registerNumber and name), and have a variable for this class's object in Employee, annotate it with #EmbeddedId.
EmployeeIdClass
#Embeddable
public class EmployeeIdClass implements Serializable{
#Column(name = "REGISTER_NUMBER", nullable = false, length = 100)
private String registerNumber;
#Column(name = "NAME", nullable = false, length = 50)
private String name;
public String getRegisterNumber() {
return registerNumber;
}
public void setRegisterNumber(String registerNumber) {
this.registerNumber = registerNumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeIdClass that = (EmployeeIdClass) o;
return Objects.equals(registerNumber, that.registerNumber) &&
Objects.equals(name, that.name);
}
#Override
public int hashCode() {
return Objects.hash(registerNumber, name);
}
}
Employee
#Entity
#Table(name = "EMPLOYEE")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
#Column(name = "employeeId", nullable = false, length = 50)
private EmployeeIdClass employeeId;
#Column(name = "EMAIL", nullable = false, length = 50)
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public EmployeeIdClass getEmployeeId() {
return employeeId;
}
public void setEmployeeId(EmployeeIdClass employeeId) {
this.employeeId = employeeId;
}
}
Company
#Entity
#Table(name = "COMPANY")
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
// #EmbeddedId
// private Employee employeeId;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
#GenericGenerator(name = "native", strategy = "native")
#Column(name = "ID", unique = true, nullable = false, precision = 38)
private Long id;
#Column(name = "NAME", nullable = false, length = 50)
private String name;
// #ToString.Exclude
// #EqualsAndHashCode.Exclude
#OneToMany(fetch = FetchType.LAZY)
private Set<Employee> employees;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Employee> getEmployees() {
return employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
}
CompanyEmployeeTest
public class CompanyEmployeeTest {
public static void main(String[] args) {
EmployeeIdClass employeeIdClass2 = new EmployeeIdClass();
employeeIdClass2.setName("b");
employeeIdClass2.setRegisterNumber("2");
EmployeeIdClass employeeIdClass3 = new EmployeeIdClass();
employeeIdClass3.setName("c");
employeeIdClass3.setRegisterNumber("3");
Employee emp2 = new Employee();
emp2.setEmail("b#");
Employee emp3 = new Employee();
emp3.setEmail("c#");
emp2.setEmployeeId(employeeIdClass2);
emp3.setEmployeeId(employeeIdClass3);
Company company = new Company();
Set<Employee> set = new HashSet<>();
set.add(emp2);
set.add(emp3);
company.setEmployees(set);
company.setName("first-company");
company.setId(1234l);
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.save(company);
session.save(emp3);
session.save(emp2);
transaction.commit();
session.close();
}
}
Queries ran by Hibernate
Hibernate: create table COMPANY (ID bigint not null auto_increment, NAME varchar(50) not null, primary key (ID)) engine=MyISAM
Hibernate: create table COMPANY_EMPLOYEE (Company_ID bigint not null, employees_NAME varchar(50) not null, employees_REGISTER_NUMBER varchar(100) not null, primary key (Company_ID, employees_NAME, employees_REGISTER_NUMBER)) engine=MyISAM
Hibernate: create table EMPLOYEE (NAME varchar(50) not null, REGISTER_NUMBER varchar(100) not null, EMAIL varchar(50) not null, primary key (NAME, REGISTER_NUMBER)) engine=MyISAM
Hibernate: alter table COMPANY_EMPLOYEE add constraint UK_lnmh1scqoa65gxcryjyeroyj unique (employees_NAME, employees_REGISTER_NUMBER)
Hibernate: alter table COMPANY_EMPLOYEE add constraint FKkkjhj0prbvia5yiqebnefkkb5 foreign key (employees_NAME, employees_REGISTER_NUMBER) references EMPLOYEE (NAME, REGISTER_NUMBER)
Hibernate: alter table COMPANY_EMPLOYEE add constraint FKivik2ern4s4074u9eb3c6u7jw foreign key (Company_ID) references COMPANY (ID)
Hibernate: insert into COMPANY (NAME) values (?)
Hibernate: insert into EMPLOYEE (EMAIL, NAME, REGISTER_NUMBER) values (?, ?, ?)
Hibernate: insert into EMPLOYEE (EMAIL, NAME, REGISTER_NUMBER) values (?, ?, ?)
Hibernate: insert into COMPANY_EMPLOYEE (Company_ID, employees_NAME, employees_REGISTER_NUMBER) values (?, ?, ?)
Hibernate: insert into COMPANY_EMPLOYEE (Company_ID, employees_NAME, employees_REGISTER_NUMBER) values (?, ?, ?)
I have 2 SQL tables:
Users:
CREATE TABLE users
(
id BIGINT PRIMARY KEY DEFAULT nextval('global_seq'),
/* email, password, other fields */
);
Users_avatars:
CREATE TABLE users_avatars
(
user_id BIGINT NOT NULL,
file_name VARCHAR,
file_path VARCHAR,
FOREIGN KEY (user_id) REFERENCES users (id)
);
However, why I try to map it with Hibernate it creates file_name and file_path inside the users table.
My classes are the following:
#Entity
#Table(name = "users")
#SecondaryTable(name = "users_avatars",
pkJoinColumns = #PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "id"))
public class User extends EntityWithId
{
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "file_name", column = #Column(table = "users_avatars")),
#AttributeOverride(name = "file_path", column = #Column(table = "users_avatars"))
})
private FileInDb avatar;
public FileInDb getAvatar()
{
return avatar;
}
public void setAvatar(FileInDb avatar)
{
this.avatar = avatar;
}
}
And FileInDb class:
#Embeddable
#MappedSuperclass
public abstract class FileInDb
{
#Column(name = "file_name")
#NotNull
#NotBlank
private String fileName;
#Column(name = "file_path")
#NotNull
#NotBlank
private String filePath;
public String getFileName()
{
return fileName;
}
public void setFileName(String fileName)
{
this.fileName = fileName;
}
public String getFilePath()
{
return filePath;
}
public void setFilePath(String filePath)
{
this.filePath = filePath;
}
}
SQL script generated by Hibernate:
create table users (
id int8 not null,
file_name varchar(255),
file_path varchar(255),
/* Lots of other fields */
primary key (id)
)
create table users_avatars (
user_id int8 not null,
primary key (user_id)
)
Why is so? Please help. Thanks in advance.
You can use #OneToOne mapping with #MapsId to use the user_id as the id field for the user_avatars table
I made an POST mapping myapp.com/users in my Spring App. When I send data to this mapping, I need create User and his Profile. Relation between them is OneToOne. Example:
AbstractEntity:
#MappedSuperclass
public abstract class AbstractEntity implements Serializable {
#Id
#GeneratedValue
private Integer id;
}
User: (Only important stuff)
#Entity
#Table(name = "Users")
public class User extends AbstractEntity {
#Basic(optional = false)
#Column(nullable = false, unique = true)
private String username;
#Basic(optional = false)
#Column(nullable = false)
private String password;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
private Profile profile;
public Profile getProfile() {
return profile;
}
public void setProfile(Profile profile) {
this.profile = profile;
}
}
Profile: (Only important stuff)
#Entity
#Table(name = "Profiles")
public class Profile extends AbstractEntity {
#Basic(optional = false)
#Column(nullable = false)
private String name;
#Basic(optional = false)
#Column(nullable = false)
private String surname;
#OneToOne
#JoinColumn(name = "USER_ID")
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
And my RestController:
#RestController
#RequestMapping("/users")
public class UserController {
private static final Logger LOG = LoggerFactory.getLogger(UserController.class);
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* Registers a new user.
*
* #param user User data
*/
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> register(#RequestBody User user) {
try {
userService.persist(user);
LOG.debug("User {} successfully registered.", user);
final HttpHeaders headers = RestUtils.createLocationHeaderFromCurrentUri("/current");
return new ResponseEntity<>(headers, HttpStatus.CREATED);
}
catch (TransactionSystemException e) {
throw new PersistenceException(e.getOriginalException());
}
}
}
How can I send both User and Profile? #RequestBody can receive only one Entity what I know. I tried this JSON data:
{
"username": "admin",
"password": "1234",
"profile":{
"name": "User",
"surname": "Test"
}
}
But even User and Profile are created, there is no relation between them (USER_ID not set).
Here are the table definitions:
CREATE TABLE `profiles` (
`ID` int(11) NOT NULL,
`BIRTHDATE` date DEFAULT NULL,
`DESCRIPTION` varchar(255) DEFAULT NULL,
`GENDER` varchar(255) DEFAULT NULL,
`IMAGE` varchar(255) DEFAULT NULL,
`NAME` varchar(255) NOT NULL,
`SURNAME` varchar(255) NOT NULL,
`USER_ID` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `sequence` (
`SEQ_NAME` varchar(50) NOT NULL,
`SEQ_COUNT` decimal(38,0) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `sequence` (`SEQ_NAME`, `SEQ_COUNT`) VALUES
('SEQ_GEN', '50');
CREATE TABLE `users` (
`ID` int(11) NOT NULL,
`BLOCKED` tinyint(1) NOT NULL DEFAULT 0,
`EMAIL` varchar(255) NOT NULL,
`PASSWORD` varchar(255) NOT NULL,
`ROLE` varchar(255) DEFAULT NULL,
`USERNAME` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `profiles`
ADD PRIMARY KEY (`ID`),
ADD KEY `FK_profiles_USER_ID` (`USER_ID`);
ALTER TABLE `sequence`
ADD PRIMARY KEY (`SEQ_NAME`);
ALTER TABLE `users`
ADD PRIMARY KEY (`ID`),
ADD UNIQUE KEY `EMAIL` (`EMAIL`),
ADD UNIQUE KEY `USERNAME` (`USERNAME`);
ALTER TABLE `profiles`
ADD CONSTRAINT `FK_profiles_USER_ID` FOREIGN KEY (`USER_ID`) REFERENCES `users` (`ID`);
I assume you have an attribute annotated with #Id.
You have to add the mappedBy to the owner of the relationship, in your case User. Try the following modifications
#Entity
#Table(name = "Users")
public class User extends AbstractEntity {
#Basic(optional = false)
#Column(nullable = false, unique = true)
private String username;
#Basic(optional = false)
#Column(nullable = false)
private String password;
//Edited here
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
private Profile profile;
public Profile getProfile() {
return profile;
}
public void setProfile(Profile profile) {
this.profile = profile;
}
}
And
#Entity
#Table(name = "Profiles")
public class Profile extends AbstractEntity {
#Basic(optional = false)
#Column(nullable = false)
private String name;
#Basic(optional = false)
#Column(nullable = false)
private String surname;
//Edited here
#OneToOne
#JoinColumn
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Can anyone help me with this and tell me what I'm missing. Have gone through a number of examples and seem to have everything configured correctly but I keep getting this exception:
org.hibernate.AnnotationException: A Foreign key refering com.bank.entity.Customer from com.bank.entity.Account has the wrong number of column. should be 2
I have a class called Branch that has 1:M relationship with Customer. Customer in turn has a 1:M relationship with Account.
Note: Customer also has an embeddable Address class
Here is my code:
Branch Class
#Entity
#Table(name = "Branch")
public class Branch extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "branch_Name")
private String branchName;
#OneToMany(mappedBy = "branch")
private Set<Customer> customers;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Embeddable Address Class
#Embeddable
public class Address {
#Column(name = "houseNumber", nullable = false)
private String houseNumber;
#Column(name = "streetName", nullable = false)
private String streetName;
#Column(name = "city", nullable = false)
private String city;
#Column(name = "country", nullable = false)
private String country;
#Column(name = "eirCode", nullable = false)
private String eirCode;
}
Customer Class
#Entity
#Table(name = "Customer")
public class Customer extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "first_Name")
private String firstName;
#Column(name = "surname")
private String surName;
#Embedded
Address address;
#ManyToOne
#JoinColumn(name = "branchId", nullable = false)
private Branch branch;
#OneToMany(mappedBy = "customer")
private Set<Account> accounts;
}
Account Class
#Entity
#Table(name = "Account")
public class Account extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "account_type")
private String type;
#Column(name = "interest_rate")
private double rate;
#Column(name = "account_balance")
private double balance;
#ManyToOne
#JoinColumn(name = "customerId", nullable = false)
private Customer customer;
}
Here I create the tables
CREATE TABLE IF NOT EXISTS `Branch` (
`id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`branch_Name` VARCHAR(25) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `Customer` (
`id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`first_Name` VARCHAR(25) NOT NULL,
`surname` VARCHAR(25) NOT NULL,
`houseNumber` VARCHAR(25) NOT NULL,
`streetName` VARCHAR(120) NOT NULL,
`city` VARCHAR(25) NOT NULL,
`country` VARCHAR(25) NOT NULL,
`eirCode` VARCHAR(25) NOT NULL,
`branchId` BIGINT(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_CUST_BRANCH` (`branchId`),
CONSTRAINT `FK_CUST_BRANCH` FOREIGN KEY (`branchId`) REFERENCES `Branch` (`id`)
);
CREATE TABLE IF NOT EXISTS `Account` (
`id` BIGINT(10) NOT NULL AUTO_INCREMENT,
`account_type` VARCHAR(25) NOT NULL,
`interest_rate` DOUBLE NOT NULL,
`account_balance` DOUBLE NOT NULL,
`customerId` BIGINT(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_CUST_ACC` (`customerId`),
CONSTRAINT `FK_CUST_ACC` FOREIGN KEY (`customerId`) REFERENCES `Customer` (`id`)
);
In Account you are saying :
#ManyToOne
#JoinColumn(name = "customerId", nullable = false)
private Customer customer;
But there is not column with name customerId(?) so you should give name to primary key of Customer
try changing this in Customer
#Entity
#Table(name = "Customer")
public class Customer extends AbstractPersistable<Long> implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="customerId")
private Long id;
...
}
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.