JPA entities cannot create the correct db tables - java

we are having a slight issue with our application
we are trying to get a login page set up and run with glassfish so that can do the authentication, in our test application we could do this but we used tables :
create table person(
username varchar(128) NOT NULL CONSTRAINT USER_PK PRIMARY KEY ,
password varchar(128) NOT NULL
..... many more fields here but are just for dob etc
);
and
create table usergroup(
username varchar(128) NOT NULL,
groupname varchar(128) NOT NULL,
CONSTRAINT GROUP_PK PRIMARY KEY(username, groupname),
CONSTRAINT USER_FK FOREIGN KEY(username) REFERENCES person(username)
ON DELETE CASCADE ON UPDATE RESTRICT
);
this worked well but for this application we are trying to get the entities to manage everything and create the tables, the issue we are having is with the constraints we set up, how can we set this up in the entites ?
The person entities
#Entity
public class Person implements Serializable {
#ManyToOne
private Address address;
#OneToMany
private List<UserGroup> userGroupList;
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName, surname, username, password, telephoneNumber, emailAddress,gender;
#Temporal(TemporalType.DATE)
private Date DateOfBirth;
public Person() {
}
public void addToGroups(UserGroup ug) {
this.userGroupList.add(ug);
}
and the usergroup entity
#Entity
public class UserGroup implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "USERNAME", referencedColumnName = "USERNAME", insertable = false, updatable = false)
#ManyToOne(optional = false)
private Person person;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username, groupName;
we need the constraints and settings as with out these are we not able to authenticate correctly using glassfish

Related

Repeated column in mapping for entity in #OneToOne

I have two tables:
CREATE TABLE users
(
id INTEGER PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(68) NOT NULL,
oldPassword VARCHAR(68),
enabled BOOLEAN NOT NULL
);
and
CREATE TABLE authorities (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
FOREIGN KEY (username) REFERENCES users(username)
);
CREATE UNIQUE INDEX ix_auth_username on authorities (username,authority);
Unfortunatelly instead of joining with authority_id which should be in users table I just have username which is the same in both tables.
In models I tried (ommited getters and setters):
#Entity(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String oldPassword;
private boolean enabled;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "username", referencedColumnName = "username")
private Authorities authority;
}
and
#Entity
public class Authorities {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String authority;
#OneToOne(mappedBy = "authority")
private User user;
}
but then I have an error: Repeated column in mapping for entity column: username (should be mapped with insert="false" update="false")
The reason you are getting usernames in both tables is that you have declared username column in both the tables.
Also, you have to map the relation based on the authority_id so the code of the models would look like this:
#Entity(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String oldPassword;
private boolean enabled;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "authority_id", referencedColumnName = "id")
private Authorities authority;
}
#Entity
public class Authorities {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String authority;
#OneToOne(mappedBy = "authority")
private User user;
}
And the tables will be created like this:
Hope this solves your problem.
The message is clear: you have a repeated column in the mapping, and a column "username" in both tables has repeated. Instead of this, you should use this :
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "authority_username", referencedColumnName = "username")
private Authorities authority;

Spring Data, Hibernate - One to Many relationship does not persist for children

I have a One-Many relationship set between two entities - A workoutSession (parent) and an Exercise(child), a workout having many exercises.
However, when I try to save the Workout session, I get the error:
ERROR: duplicate key
value violates unique constraint "exercise_pkey" Detail: Key
(id)=(21) already exists.
I do not understand why an exercise does not have a unique key generated here ?
#Entity
#Table(name="workout")
public class Workout {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String name;
private String category;
private String type;
private Timestamp duration;
private String notes;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "id")
private List<Exercise> exercises;
}
#Entity
#Table(name="exercise")
public class Exercise {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="name")
private String exerciseName;
}
table create scripts:
CREATE TABLE WORKOUT(
ID serial PRIMARY KEY NOT NULL,
NAME CHAR(50) NOT NULL,
CATEGORY CHAR(50),
TYPE CHAR(50),
DURATION TIMESTAMP,
NOTES CHAR(500)
);
CREATE TABLE exercise (
id serial primary key not null,
workout integer references workout(id),
name char(100) NOT NULL
);

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.

Composite keys with Hibernate

I need help to create the correct pojo's from this database...
https://www.dropbox.com/s/j2lfu44zpqfcxb4/dbr.PNG
I have tried creating this classes...
#Entity
#Table(name="Municipio", catalog="elecciones2014", schema="")
public class Municipio implements Serializable{
#EmbeddedId
private MunicipioPk idMunicipio;
#Basic(optional=false)
#Column(name="nomb_municipio")
private String nomb_municipio;
}
With this Embedded class
#Embeddable
class MunicipioPk implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name="id_depto")
String departamento;
#Column(name="id_municipio")
String idMunicipio;
}
The problem is when i want to reference to 'Municipio' from 'JRV' y don't know how to access to field 'id_municipio'. I had this code but it doesn't work
#Entity
#Table(name = "JRV", catalog = "elecciones2014", schema = "")
public class Jrv {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_jrv")
private int id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="id_municipio",referencedColumnName="idMunicipio")
private Municipio municipio;
#ManyToOne
#JoinColumn(name="DUI",referencedColumnName="dui")
private PadronElectoral dui;
}
can someone help me?
how I have to do it?
Thanks in advice!!
Here you are defining single join column, but the Municipio entity's PK has two columns. Also the referencedColumnName should be the name of the column not the entity's property.
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="id_municipio",referencedColumnName="idMunicipio")
private Municipio municipio;
So you could do something like this:
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="id_municipio", referencedColumnName="id_municipio"),
#JoinColumn(name="id_depto", referencedColumnName="id_depto")
})
private Municipio municipio;
Which translates to this SQL (I got this by generating SQL schema from your entities after the modification mentioned above):
create table elecciones2014.JRV (
id_jrv serial not null,
id_depto varchar(255),
id_municipio varchar(255),
primary key (id_jrv)
);
alter table elecciones2014.JRV
add constraint FK_7scd8alu3nf4tsyh3hq2ryrja
foreign key (id_depto, id_municipio)
references elecciones2014.Municipio;

Hibernate #OneToMany without a separate join table

Consider the following database schema:
create table UserGroup ( id int not null auto_increment, name varchar(200),
primary key(id));
create table User ( id int not null auto_increment, name varchar(200),
groupId int not null, primary key(id));
User.groupId = UserGroup.id, so a user can only be a member of one group, but a usergroup can exist of many users. Fine so far, let's make the entities in Hibernate. Here's User:
#Entity
#Table(name = "User")
public class User {
#Id
#Column(name="id", nullable = false)
private Integer id;
#Column(name="name", length = 200, nullable = true)
private String name;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "groupId", nullable = false, insertable=false, updatable=false)
#ForeignKey(name="FK_GroupId")
private UserGroup userGroup;
/* Getters, Setters, toString, equals & hashCode */
}
Here's UserGroup:
#Entity
#Table(name = "UserGroup")
public class UserGroup {
#Id
#Column(name="id", nullable = false)
private Integer id;
#Column(name="name", length = 200, nullable = true)
private String name;
#OneToMany(fetch=FetchType.EAGER)
private List<User> users;
/* Getters, Setters, toString, equals & hashCode */
}
Now I'll get an error "Table mydb.usergroup_user' doesn't exist" because it expects a join-table. My data structure is "set in stone" due to interoperability with other applications that this application will replace, so I won't be making a join-table. Also, it should not be needed. How can I make a List<User> users that simply is a list of User where User.groupId == UserGroup.Id?
I think you need the mappedBy="UserGroup" in the #OneToMany annotation.

Categories