Join column from another table with Foreign Key using JPA - java

I'm new to JPA and trying to understand if there's a way to make an Entity where one column is coming from another table that is linked by a foreign key. For example, consider the following tables:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `jobs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11),
PRIMARY KEY (`id`),
CONSTRAINT `fk_jobs_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
);
Now I want to make an Entity for the "jobs" table that will include the user.email. I know I can do something like
#Entity
#Table(name = "jobs")
public class JobEntity {
#Id
#Column(name = "id")
private Long id;
#Column(name = "user_id")
private Long userId;
#Formula("(select user.email FROM user WHERE user.id = user_id)")
private String userEmail;
But I feel there's a way I can better leverage the foreign key relationship, but I'm not sure how. I was looking into #JoinColumn but was not seeing the result I wanted since the foreign key is a different column in my Entity. Is there a better way rather than using #Forula to do this?

I don't really understand this. I'm sure #JoinColumn can accomplish the behavior you're looking for.
I was looking into #JoinColumn but was not seeing the result I wanted since the foreign key is a different column in my Entity
Example:
#Entity
#Table(name = "jobs")
public class KronosFileEntity {
#Id
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumn = "id")
private User user;
}
Then you can access the email like job.getUser().getEmail()
Or add a convenience method if that helps
public String getUserEmail() {
return user.getEmail();
}
Then
job.getUserEmail()

Related

Unable to add multiple entities in ManyToOne mapping due to Unique Key being created

I have been fighting this issue regarding two entities: User and ProfileInformation.
Each user should hold many ProfileInformation instances but I get the following error when creating a second ProfileInformation: Duplicate entry 'usernameInQuestion' for key 'UK_141232asdas78k552'.
When checking the database schema for ProfileInformation I noticed that the 'username' field was created as a unique key:
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_14113pekuxo8jklda2678k552` (`username`),
CONSTRAINT `FKt3ewldgdc9d75ji6j5ii7cbrc` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
I have a very similar relation between ProfileInformation and another entity with the same code (except the naming of the classes and #JoinColumn(name = "profileinformation_id") ) and this doesn't happen:
`profileinformation_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FKl5ww2gn1e1wgqcuvraa6p70ya` (`profileinformation_id`),
CONSTRAINT `FKl5ww2gn1e1wgqcuvraa6p70ya` FOREIGN KEY (`profileinformation_id`) REFERENCES `profileinformationtable` (`id`)
This is part of a bigger system and between all the confusion of making this work I attempted to replicate the already working similar situations.
public class ProfileInformation
[ ... ]
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name="username")
private User user;
The User class is the following (although from what I gather this really doesn't seem to matter much):
public class User
[ ... ]
#Id
#Column(name = "username", nullable = false)
private String username;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
private List<ProfileInformation> infos;

JPA Join tables Query

I am working on a school project, and I am having trouble with joining tables so I can display output in JSP file using JSTL. I will provide all necessary code. I know that I need to connect entities somehow, but I don't know how.
SQL:
CREATE TABLE IF NOT EXISTS `totelegram`.`contacts` (
`id` INT NOT NULL AUTO_INCREMENT,
`first_name` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci' NOT NULL,
`last_name` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci' NOT NULL,
`phone_number` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id_UNIQUE` (`id` ASC),
UNIQUE INDEX `phone_number_UNIQUE` (`phone_number` ASC))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `totelegram`.`messages` (
`id_message` INT NOT NULL AUTO_INCREMENT,
`message` VARCHAR(2000) CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci' NOT
NULL,
`time` VARCHAR(45) NOT NULL,
`contacts_id` INT NOT NULL,
PRIMARY KEY (`id_message`),
UNIQUE INDEX `id_message_UNIQUE` (`id_message` ASC),
INDEX `fk_messages_contacts_idx` (`contacts_id` ASC),
CONSTRAINT `fk_messages_contacts`
FOREIGN KEY (`contacts_id`)
REFERENCES `totelegram`.`contacts` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Contacts.java
#Entity(name = "contacts")
public class Contacts implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#javax.persistence.Column(name = "first_name")
private String firstName;
#javax.persistence.Column(name = "last_name")
private String lastName;
#javax.persistence.Column(name = "phone_number")
private String phoneNumber;
...getters/setters, constructor, toString...
Messages.java
#Entity(name = "messages")
public class Messages implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#javax.persistence.Column(name = "id_message")
private int id;
private String message;
private String time;
#javax.persistence.Column(name = "contacts_id")
private int contactsId;
...getters/setters, constructor, toString...
MessagesRepository.java
public interface MessagesRepository extends JpaRepository<Messages, Integer> {
//custom query which will output this
//SELECT b.message, b.time, b.contacts_id, a.first_name, a.last_name FROM messages AS b INNER JOIN contacts as A ON (b.contacts_id=a.id) ORDER BY time ASC;
public List<Messages> findAll();
}
I hope I was clear. Thanks to everybody in advance.
As far as i understand, one contact can have N messages and you cannot have a Message without the Contact, right?
Since you have relations between classes, you have to use specific annotations in jpa, for example:
in the Message Class, you should use the #ManyToOne annotation, since you have Many Messages for One Contact. The JoinColumn will input the contacts_id in the Messages Table.
#ManyToOne
#JoinColumn(name = "contacts_id")
private Contacts contact;
in the Contacts Class, you should use #OneToMany annotation, since One Contact has Many Messages. The mappedBy makes a reference in contact at the Message Class.
#OneToMany(mappedBy = "contact")
private List<Messages> messages = new ArrayList<>();
So far you made a Bidirectional reference between Contacts and Messages. Now in your service class, i would recommend you find the Messages through the Contacts, since you cannot have a message without the contact. Its a Repository principle.
Contacts con = repository.findOne(1);
con.getMessages();
btw, sorry for the bad english.

Map entity with #SecondaryTable Annotation Spring Hibernate

In my db I have two tables which look like this:
CREATE TABLE IF NOT EXISTS `Lokal` (
`idLokal` int(11) NOT NULL AUTO_INCREMENT,
`Ocena_idOcena` int(11) NOT NULL,
PRIMARY KEY (`idLokal`,`Ocena_idOcena`),
KEY `fk_Lokal_Ocena_idx` (`Ocena_idOcena`)
)
CREATE TABLE IF NOT EXISTS `Ocena` (
`idOcena` int(11) NOT NULL AUTO_INCREMENT,
`Ocena` int(1) DEFAULT NULL,
PRIMARY KEY (`idOcena`)
)
I want to map my Lokal entity to this Ocena table using #SecondaryTable Hibernate annotation, what I managed to achieve is this:
#Entity
#Table(name="Lokal")
#SecondaryTable(name = "Ocena", pkJoinColumns=#PrimaryKeyJoinColumn(name="Ocena_idOcena"))
public class Lokal {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="idLokal")
private int id;
#Column(table="Ocena" ,name="idOcena")
private int rating;
//--Getters and Setters skipped--//
}
But all I get is an error saying:
ERROR: Unknown column 'this_1_.Ocena_idOcena' in 'on clause'
I think I'm misunderstanding the #SecondaryTable annotation, but this is my first Spring/Hibernate application so I'd be glad for any kind of help.
Try this:
#Entity
#Table(name="Lokal")
#SecondaryTable(name = "Ocena", pkJoinColumns=#PrimaryKeyJoinColumn(name="idOcena"))
public class Lokal {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="idLokal")
private int id;
#Column(table="Ocena" ,name="rating")
private int rating;
//--Getters and Setters skipped--//
}

JPA: Is Map<Entity1,Set<Entity2>> possible?

I don't see an example anywhere so I am not sure this is possible. But basically, I am trying to see if I can bind a field in an entity to
Map<Skill,Set<Rating>> ratings;
CREATE TABLE Worker (
ID BIGINT PRIMARY KEY,
);
CREATE TABLE Skill (
ID BIGINT PRIMARY KEY,
name VARCHAR(32) NOT NULL,
UNIQUE (name)
);
CREATE TABLE WorkerSkillRating (
ID BIGINT PRIMARY KEY,
WorkerID BIGINT NOT NULL,
SkillID BIGINT NOT NULL,
Rating INT,
FOREIGN KEY (WorkerID) REFERENCES Worker (ID),
FOREIGN KEY (SkillID) REFERENCES Skill (ID),
FOREIGN KEY (Rating) REFERENCES Rating (ID)
);
CREATE TABLE Rating (
ID BIGINT PRIMARY KEY,
score TINYINT NOT NULL,
comments VARCHAR(256)
);
Entities
#Entity
public class Skill {
#Id
private Long id;
private String name;
public Skill(String name) {
this();
this.name = name;
}
public Skill() {
this.id = Math.abs( new Random().nextLong());
}
}
#Entity
public class Worker {
#Id
private Long id;
// The open question
public Map<Skill, Set<Rating>> ratings;
}
#Entity
public class Rating {
#Id
private Long id;
private Byte score;
private String comments;
}
According to the JSR-0038 the JPA spec. When using Map, the following combination are just allowed: Basic Type, Entities and Embeddables.
Map<Basic,Basic>
Map<Basic, Embeddable>
Map<Basic, Entity>
Map<Embeddable, Basic>
Map<Embeddable,Embeddable>
Map<Embeddable,Entity>
Map<Entity, Basic>
Map<Entity,Embeddable>
Map<Entity, Entity>
I don’t think there is pretty much deal to have a possible mapping in the way that you want but that is out of the specs and most of the providers follow them, I think that mapping is not very common at all.
"worker has many skills and he may have been given many ratings on a
single skill. "
Then add to the skill class a Set<Ratings>, instead of nested directly in the map as the value of it.
It might not answer your question with the map but...
It looks like your rating table is unnecessary.
You could instead have
CREATE TABLE Worker (
ID BIGINT PRIMARY KEY,
);
CREATE TABLE Skill (
ID BIGINT PRIMARY KEY,
name VARCHAR(32) NOT NULL,
UNIQUE (name)
);
CREATE TABLE WorkerSkill (
ID BIGINT PRIMARY KEY,
WorkerID BIGINT NOT NULL,
SkillID BIGINT NOT NULL,
score TINYINT NOT NULL,
comments VARCHAR(256)
FOREIGN KEY (WorkerID) REFERENCES Worker (ID),
FOREIGN KEY (SkillID) REFERENCES Skill (ID)
);
Note I moved the rating information to WorkerSkill table.
Then you can map your entities per below
#Entity
public class Skill {
#Id
private Long id;
private String name;
// Getter setters const etc
}
#Entity
public class WorkerSkill {
#Id
private Long id;
private int score;
private String comments;
#ManyToOne
private Skill skill;
#ManyToOne
private Worker worker;
// Getter setters const etc
}
#Entity
public class Worker {
#Id
private Long id;
#OneToMany
public List<WorkerSkill> workerSkills = new ArrayList<>();
// Getter setters const etc
}
Then you can access all worker's skill using worker.getWorkerSkill();

Can I create an #ElementCollection of #Entity?

I'm trying to find relevant JPA documentation, but having trouble finding anything that specifies if I am allowed to make an ElementCollection of an Entity. I know the typical use is to make an #ElementCollection of #Embeddable, but due to a Hibernate bug I encountered, I need to make my embeddable class into its own entity.
I would like to maintain the entity's lifecycle to be controlled by the parent class. Consequently, I am hoping not to create any DAO/Repository for the new entity.
Although Hibernate allows this to occur (it properly generates the DDL), I have not actually tested persistence of the parent entity yet.
Additionally, I have not been able to find any way to specify the join column mapping to the new entity.
For example, given:
public class User {
#TableGenerator( name="UUIDGenerator", pkColumnValue="user_id", table="uuid_generator", allocationSize=1)
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator="UUIDGenerator")
#Column(name = "id")
private Long id;
/**
* Address
*/
#Valid
#ElementCollection(fetch=FetchType.LAZY)
#CollectionTable(name="user_address", joinColumns=#JoinColumn(name = "user_id", referencedColumnName = "id"))
#OrderColumn
private List<Address> address;
}
and
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
/**
* Multiple street lines allowable
*/
#NotBlank
#ElementCollection(targetClass=String.class, fetch=FetchType.LAZY)
#CollectionTable( joinColumns=#JoinColumn(name = "address_id", referencedColumnName = "id"))
#OrderColumn
private List<String> street;
}
it generates the following MySQL tables:
CREATE TABLE `user` (
`id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_address` (
`user_id` bigint(20) NOT NULL,
`address` bigint(20) NOT NULL,
`address_order` int(11) NOT NULL,
PRIMARY KEY (`user_id`,`address_order`),
UNIQUE KEY `UK_m09f5sbmw3q9drll2qig9i07q` (`address`),
KEY `FK_m09f5sbmw3q9drll2qig9i07q` (`address`),
KEY `FK_kfu0161nvirkey6fwd6orucv7` (`user_id`),
CONSTRAINT `FK_kfu0161nvirkey6fwd6orucv7` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
CONSTRAINT `FK_m09f5sbmw3q9drll2qig9i07q` FOREIGN KEY (`address`) REFERENCES `address` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `address` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `address_street` (
`address_id` bigint(20) NOT NULL,
`street` varchar(255) DEFAULT NULL,
`street_order` int(11) NOT NULL,
PRIMARY KEY (`address_id`,`street_order`),
KEY `FK_jrcnrclixxqroefuqc7gjhoh` (`address_id`),
CONSTRAINT `FK_jrcnrclixxqroefuqc7gjhoh` FOREIGN KEY (`address_id`) REFERENCES `address` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I'd like to find a way to change the field name for user_address.address. So far, the only way I can figure out how to do this is to make it a #OneToMany mapping and use a #JoinTable() definition. Is there no way from within #ElementCollection?
You cannot have an ElementCollection containing Entities, these are mutually exclusive concepts in JPA.
You can try a one-to-many or many-to-many depending on your needs. If you set fetching to eager and cascade all it should be fine.

Categories

Resources