Hibernate ignores #AttributeOverrides annotation and creates columns in a wrong table - java

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

Related

Spring REST API - User + his profile persist

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;
}
}

Using RowMapper and JdbcTemplate got NullPointerException

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.

Spring JPA Repository query filter by a relationship table

If I have a many-to-many relationship between JPA entities as below, how can I retrieve a list of Person (I am interested in the person attributes) that are employees of a specific company?
The relationship between Person and Company is many-to-many. The relationship table Employee has the FK to Person and Company, and a start_date and end_date to indicate when the employment started and finished.
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
}
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
}
#Entity
public class CompanyEmployee {
//note this is to model a relationship table. Am I doing this wrong?
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "start_date", nullable = false)
private LocalDate startDate;
#Column(name = "end_date", nullable = false)
private LocalDate endDate;
#ManyToOne
private Company company;
#ManyToOne
private Person person;
}
Do I use a #Query on the CompanyEmployeeJPARepository? How should I tackle it?
public interface CompanyEmployeeRepository extends JpaRepository<CompanyEmployee,Long> {
//
}
Pablo,
Our company is in the process of converting our existing Spring / MyBatis code to Spring Data JPA, so I have been learning Spring Data JPA for a few weeks. I'm clearly not an expert, but I worked out an example similar to yours which may help you.
I have Person and Company classes that are similar to yours, but (as Jens mentioned), you need lists with OneToMany annotations. I used a separate join table (named company_person) which only has companyId, personId columns to maintain the many-to-many relationship. See the code below.
I did not see a way to put the start/end dates in the company_person join table, so I made a separate (4th table) for that. I called it employment_record with Java class entity EmploymentRecord. It has the combo primary key (companyId, personId) and the start/end dates.
You need repositories for Person, Company, and EmploymentRecord. I extended CrudRepository instead of JpaRepository. But, you don't need an entity or repository for the join table (company_record).
I made a Spring Boot Application class to test it out. I used CascadeType.ALL on Person's OneToMany. In my Application test, I tested that I can change the companies assigned to a person and Spring Data propagates all the changes needed to the Company entities and join table.
However, I had to manually update the EmploymentRecord entities, via its repository. For example, I had to add a start_date each time I added a company to a person. Then, add an end_date when I removed that company from that person. There is probably some way to automate this. The Spring / JPA audit feature is a possibility, so check that out.
The answer to your question:
how can I retrieve a list of Person (I am interested in the person
attributes) that are employees of a specific company?
You simply use companyRepository's findOne(Long id) method followed by getPersonList() method.
snippet from Application.java:
PersonRepository pRep = context.getBean(PersonRepository.class);
CompanyRepository cRep = context.getBean(CompanyRepository.class);
EmploymentRecordRepository emplRep = context.getBean(EmploymentRecordRepository.class);
...
// fetch a Company by Id and get its list of employees
Company comp = cRep.findOne(5L);
System.out.println("Found a company using findOne(5L), company= " + comp.getName());
System.out.println("People who work at " + comp.getName());
for (Person p : comp.getPersonList()) {
System.out.println(p);
}
Here are some references that I found to be useful:
Spring Data JPA tutorial
Join Table example
Person.java:
#Entity
public class Person {
// no-arg constructor
Person() { }
// normal use constructor
public Person(String name, String address) {
this.name = name;
this.address = address;
}
#Id
#GeneratedValue
private Long id;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#Version
private int versionId;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name="company_person",
joinColumns={#JoinColumn(name="person_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="company_id", referencedColumnName="id")})
private List<Company> companyList;
// Getters / setters
}
Company.java:
#Entity
public class Company {
// no-arg constructor
Company() { }
// normal use constructor
public Company(String name, String address) {
this.name = name;
this.address = address;
}
#Id
#GeneratedValue
private Long id;
#Column(name = "name")
private String name;
#Column(name = "address")
private String address;
#Version
private int versionId;
//#OneToMany(cascade=CascadeType.ALL)
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name="company_person",
joinColumns={#JoinColumn(name="company_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="person_id", referencedColumnName="id")})
private List<Person> personList;
// Getters / Setters
}
EmploymentRecord.java:
#Entity
#IdClass(EmploymentRecordKey.class)
public class EmploymentRecord {
// no-arg constructor
EmploymentRecord() { }
// normal use constructor
public EmploymentRecord(Long personId, Long companyId, Date startDate, Date endDate) {
this.startDate = startDate;
this.endDate = endDate;
this.companyId = companyId;
this.personId = personId;
}
// composite key
#Id
#Column(name = "company_id", nullable = false)
private Long companyId;
#Id
#Column(name = "person_id", nullable = false)
private Long personId;
#Column(name = "start_date")
private Date startDate;
#Column(name = "end_date")
private Date endDate;
#Version
private int versionId;
#Override
public String toString() {
return
" companyId=" + companyId +
" personId=" + personId +
" startDate=" + startDate +
" endDate=" + endDate +
" versionId=" + versionId;
}
// Getters/Setters
}
// Class to wrap the composite key
class EmploymentRecordKey implements Serializable {
private long companyId;
private long personId;
// no arg constructor
EmploymentRecordKey() { }
#Override
public int hashCode() {
return (int) ((int) companyId + personId);
}
#Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (!(obj instanceof EmploymentRecordKey)) return false;
EmploymentRecordKey pk = (EmploymentRecordKey) obj;
return pk.companyId == companyId && pk.personId == personId;
}
// Getters/Setters
}
MySql script, createTables.sql:
DROP TABLE IF EXISTS `test`.`company_person`;
DROP TABLE IF EXISTS `test`.`employment_record`;
DROP TABLE IF EXISTS `test`.`company`;
DROP TABLE IF EXISTS `test`.`person`;
CREATE TABLE `company` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL DEFAULT '',
`address` varchar(500) DEFAULT '',
`version_id` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `person` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL DEFAULT '',
`address` varchar(500) DEFAULT '',
`version_id` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/* Join table */
CREATE TABLE `company_person` (
`company_id` int NOT NULL,
`person_id` int NOT NULL,
PRIMARY KEY (`person_id`,`company_id`),
KEY `company_idx` (`company_id`),
KEY `person_idx` (`person_id`),
CONSTRAINT `fk_person` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_company` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/* Employment records */
CREATE TABLE `employment_record` (
`company_id` int NOT NULL,
`person_id` int NOT NULL,
`start_date` datetime,
`end_date` datetime,
`version_id` int NOT NULL,
PRIMARY KEY (`person_id`,`company_id`),
KEY `empl_company_idx` (`company_id`),
KEY `empl_person_idx` (`person_id`),
CONSTRAINT `fk_empl_person` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_empl_company` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I have previous experience in hibernate JPA but not spring JPA. From that knowledge following query might be useful:
select cp.person from CompanyEmployee cp where cp.company.id = ?
You shouldn't need to make a separate entity for the relationship table.
The relationship can be maintained within the two entities,
so if A and B are in a many-to-many relationship,
#Entity
class A {
#Id
Long id;
...
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(name="a_b",
joinColumns={#JoinColumn(name="id_a", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="id_b", referencedColumnName="id")})
List<B> bList;
...
}
#Entity
class B {
#Id
Long id;
...
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(name="a_b",
joinColumns={#JoinColumn(name="id_b", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="id_a", referencedColumnName="id")})
List<A> aList;
...
}
You can now use the repository queries on either of the entity repositories or if you have a query with params on both, you can create a custom query in the repository of one.

Associating two tables with hibernate annotations

I am new to Hibernate, so please forgive me if the question is duplicated or stupid. I am using Hibernate 3.3.0, PostgreSQL DB. Right now I have 2 entities. I need to associate them with #OneToMany annotation in such way, that User.roleID was PK and Role.id was FK (user can have only one role, and the same role can be assigned to many users). Also as I was searching Google, I found out that for some reason every example with annotation associations has the annotated field declared as Set. Please, explain this to me. Thank you in advice.
Nazar.
User entity:
package com.dataart.mediaportal.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "users")
public class User implements Serializable {
private int id;
private String login;
private String password;
private String firstName;
private String lastName;
private int roleID;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id", unique = true)
public int getId() {
return id;
}
#Column(name = "user_login", nullable=false, unique = true, length = 20 )
public String getLogin() {
return login;
}
#Column(name = "user_password", nullable = false, length = 32)
public String getPassword() {
return password;
}
#Column(name = "user_name", nullable = true)
public String getFirstName() {
return firstName;
}
#Column(name = "user_lastname", nullable = true)
public String getLastName() {
return lastName;
}
#Column(name = "role_id", nullable = false)
public int getRoleID() {
return roleID;
}
public void setRoleID(int roleID) {
this.roleID = roleID;
}
public void setId(int id) {
this.id = id;
}
public void setLogin(String login) {
this.login = login;
}
public void setPassword(String password) {
this.password = password;
}
public void setFirstName(String firstname) {
this.firstName = firstname;
}
public void setLastName(String lastname) {
this.lastName = lastname;
}
}
Role entity:
package com.dataart.mediaportal.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "roles")
public class Role implements Serializable {
private int id;
private String role;
#Id
#Column(name = "role_id", nullable = false)
public int getId() {
return id;
}
#Column(name = "role_name", nullable = false)
public String getRole() {
return role;
}
public void setId(int id) {
this.id = id;
}
public void setRole(String role) {
this.role = role;
}
}
DB Script:
DROP TABLE IF EXISTS users CASCADE;
DROP TABLE IF EXISTS roles CASCADE;
DROP TABLE IF EXISTS albums CASCADE;
DROP TABLE IF EXISTS images CASCADE;
CREATE TABLE users
(
user_login character varying(20) NOT NULL,
user_password character varying(32) NOT NULL,
user_name character varying,
user_lastname character varying,
role_id integer DEFAULT 0,
user_id serial NOT NULL,
CONSTRAINT pk_user_id PRIMARY KEY (user_id),
CONSTRAINT users_user_login_key UNIQUE (user_login)
)
WITH (
OIDS=FALSE
);
ALTER TABLE users
OWNER TO postgres;
CREATE TABLE roles
(
role_id integer NOT NULL,
role_name character varying NOT NULL DEFAULT 'user'::character varying,
CONSTRAINT role_id_pk PRIMARY KEY (role_id)
)
WITH (
OIDS=FALSE
);
ALTER TABLE roles
OWNER TO postgres;
INSERT INTO roles
VALUES ( 0, 'user' ),
( 1, 'admin' );
-- testlog - testpass
-- user - password
INSERT INTO users
VALUES ( 'testlog', '179ad45c6ce2cb97cf1029e212046e81', 'Nazar', 'Sobchuk', 1),
('user', '5f4dcc3b5aa765d61d8327deb882cf99', 'unknown', 'unknown', 0);
CREATE TABLE albums
(
album_name character varying NOT NULL,
user_id integer,
album_id serial NOT NULL,
CONSTRAINT album_id PRIMARY KEY (album_id),
CONSTRAINT user_id FOREIGN KEY (user_id)
REFERENCES users (user_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE albums
OWNER TO postgres;
CREATE INDEX fki_user_id
ON albums
USING btree
(user_id);
INSERT INTO albums VALUES ('Main Album', 1);
CREATE TABLE images
(
img bytea,
img_name character varying NOT NULL,
album_id integer,
img_id serial NOT NULL,
CONSTRAINT images_img_name UNIQUE(img_name),
CONSTRAINT album_id FOREIGN KEY (album_id)
REFERENCES albums (album_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE images
OWNER TO postgres;
CREATE INDEX fki_album_id
ON images
USING btree
(album_id);
Insert user:
public boolean insertUser(User user) {
factory = getSessionFactory();
session = factory.openSession();
tx = session.beginTransaction();
Query queryResult = session.createQuery("from User");
List<User> userList = queryResult.list();
for (User u : userList) {
if (u.getLogin().equalsIgnoreCase(user.getLogin())) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("User_is_already_registered"));
return false;
}
}
session.save(user);
tx.commit();
session.close();
return true;
}
Add the user as one to many relationship to Role as below. create the setters as well
private List<User> userList;
#OneToMany(mappedBy = "role")
public List<User> getUserList() {
return userList;
}
Add the Role as many to one relationship to User as below
private Role role;
#ManyToOne
public Role getRole() {
return role;
}

#ManyToOne mapping doesn't work with joined inheritance

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.

Categories