I'm trying to apply one to one relationship between two entities
first entity:
Video and OrganzationVideo every OrganizationVideo has one video entity
So I did the following
first organization_video table
CREATE TABLE `organization_video` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
video table
CREATE TABLE `video` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(100) DEFAULT NULL,
// rest of table contents
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
then I added constraint in organization_video table
CONSTRAINT `FK_organization_video` FOREIGN KEY (`id`) REFERENCES `video` (`id`)
Then generated entities
Video.java
#Entity
#Table(name = "video")
public class Video extends Persistable<Long> {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Long id;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "video")
private OrganizationVideo organizationVideo;
\\ rest of video contetns
}
OrganizationVideo.java
#Entity
#Table(name = "organization_video")
public class OrganizationVideo extends Persistable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Long id;
#JoinColumn(name = "id", referencedColumnName = "id")
#OneToOne(optional = false)
private Video video;
\\ rest of organziation video contents
}
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="Default_Persistence_Unit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>jdbc/defaultDataSource</non-jta-data-source>
<properties>
<property name="hibernate.transaction.auto_close_session" value="false"/>
<property name="hibernate.max_fetch_depth" value="0"/>
<property name="hibernate.connection.release_mode" value="auto"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.cglib.use_reflection_optimizer" value="true"/>
</properties>
</persistence-unit>
</persistence>
everything worked perfectly when persisting objects
the issue is with the fetching query
StringBuilder queryStr = new StringBuilder("select v from Video v where v.organizationVideo is null");
Query query = getEntityManager().createQuery(queryStr.toString());
return query.getResultList();
The weird behavior is that this query fetches data once then doesn't fetch any data tried to use #PrimaryKeyJoinColumn and changed id generation type with no luck but indeed there is something wrong it's strange to see data once first time when I tried to remove is null from query it fetches data but with null value assigned to OrganizationVideo then why the query doesn't work except at first time.
You will have to check the SQL that is generated for the query, but I'm guessing that since the ID is the foreign key and it is autogenerated, inserting entities causes problems with your query. Try fixing your model first:
Either the OrganizationVideo ID needs to be set by the video relationship (if using JPA 2.0):
#Entity
#Table(name = "organization_video")
public class OrganizationVideo extends Persistable<Long> {
#Id
#JoinColumn(name = "id", referencedColumnName = "id")
#OneToOne(optional = false)
private Video video;
\\ rest of organziation video contents
}
That, or you make one of the ID mappings within OrganizationVideo insertable=false, updatable=false. if you do this, then you must set the other fields with in OrganizationVideo within the application yourself, and only after the Video instance has an ID value assigned. Since you are using identity, this means you would have to persist the vidoe, flush, and then use that value within the OrganizationVideo before it can be persisted.
It would be easier for your model if you could just add a foreign key to the OrganizationVideo table and use that to reference the Video ID.
Related
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()
I'm using Hibernate in combination with phpmyadmin (MySQL).
Recently I found a really weird error.
Whenever I try to insert a new row (.persist) I get the following error:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'user_id' cannot be null
The error is really obvious.
But the weird part is, this error came out of nowhere.
I'm ENTIRELY sure the property userId (mapped to 'user_id') isn't null. I tested several times.
This is the mapping part:
(On the #manytoone part).
<property name="userId" update="false" insert="false" column="user_id" type="java.lang.Long" />
The OneToMany part isn't the problem I guess.
So the problem here is, i'm 100% sure the value isn't null, still Hibernate passes it as null to the MySQL. (Getter does work).
UPDATE
#ManyToOne side, which causes the error
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="xx.xx.xx.AlarmEntity" table="alarms">
<meta attribute="class-description">
Hierin staan de object properties van de alarm entiteit.
</meta>
<id name="id" type="java.lang.Long" column="id">
<generator class="native"/>
</id>
<property name="keepsRunning" type="java.lang.Boolean">
<column name="keeps_running" sql-type="int"></column>
</property>
<property name="userId" update="false" insert="false" column="user_id" type="java.lang.Long" />
<many-to-one name="userAlarm" cascade="save-update" fetch="select" column="user_id" class="xx.xx.xx.UserEntity" />
</class>
</hibernate-mapping>
Please check the mapping
UserEntity
private int user_id;
private Set<AlarmEntity> alarm;
AlarmEntity
private int alarm_id;
private String keepsRunning;
As per your requirement , I have provided mapping with annotation please check
User
#Id
#Column(name = "userId")
private Long userId;
// parent to Alarm
#Fetch(value = FetchMode.SELECT)
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "userId")
#JsonIgnore
private List<Alarm> alarmList ;
Alarm
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "alarmId")
private Long alarmId;
#ManyToOne
#JoinColumn(name = "userId", insertable = true, updatable = true, nullable = true)
private User user;
#Column(name="keepsRunning")
private String keepsRunning;
Providing Annotation Approach one to Many Example which worked for me
In below example Factory can have multiple product ,that means there is one to many mapping between Factory and Product where Product is owning side of this relationship which means Product will maintain foreign key constraint for holding factory id.
Table structure:
CREATE TABLE `factory` (
`id` bigint NOT NULL AUTO_INCREMENT,
`factory_name` varchar(500) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `product` (
`id` bigint NOT NULL AUTO_INCREMENT,
`factory_id` bigint NOT NULL ,
`product_name` varchar(500) DEFAULT NULL,
PRIMARY KEY (`id`)
);
ALTER TABLE product
ADD CONSTRAINT FK_product_factory
FOREIGN KEY (factory_id) REFERENCES factory (id);
Entity Structure:
#Entity
#Table(name = “factory”)
public class Factory {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name=“id”)
private int id;
#Column(name = "factory_name")
private String factoryName;
#OneToMany(mappedBy = “factory”,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private List<Product> products = new ArrayList<>();
}
#Entity
#Table(name = “product”)
public class Product {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = “product_name")
private String productName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name=“factory_id")
private Factory factory;
}
Service Structure:
#Service
public class ProductService {
#Autowired
private ProductRepository productRepository;
#Autowired
private FactoryRepository factoryRepository;
public void saveFactoryAndProduct(){
Factory factory = new Factory();
factory.setFactoryName("F1");
Product product1 = new Product();
product1.setProductName("P1");
factory.getProducts.add(product1);
product.setFactory(factory);
Product product2 = new Product();
product2.setProductName("P2");
factory.getProducts.add(product2);
product1.setFactory(factory);
//Saving factory will also saved linked product1 and product2 data having factory_id as a foreign key from Factory table
factoryRepository.save(factory);
}
}
Note:
Saving Products with Mapped Factory.Here we don't need to save product specifically since Factory is maintaining cascade so when we save factory related products will also save but we just need to setFatory for every product object inorder to maintain foreign key in product.
I know that if you want to reference back from #Embeddable to its parent you can set the parent "manually" in the setter and use #Access(AccessType.PROPERTY) for this embedded field as stated in this answer, but what if this embedded element is mapped in a collection, which is lazy loaded?
Actually not sure whether this is an issue, if not "manually" reference back from #embeddable to its parent, everything is fine.
#CollectionTable.JoinColumns() is used to set the foreign key columns of the collection table which reference the primary table of the entity, which means that once set this optional property, there is no necessary to "manually" reference back from #embeddable to its parent.
Use your case as example:
#Entity
public class Image {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
....
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "COMPUTERS", joinColumns = #JoinColumn(name = "ID_IMAGE"))
private List<Computer> computers;
}
#Embeddable
public class Computer {
#Column
private String ipAddress;
*****//This idImage field is not necessary
#Column(name = "ID_IMAGE", insertable = false, updatable = false)
private Long idImage;*****
}
Once comment out the field idImage and its #Column annotation, the generated SQL is:
create table IMAGES (
id bigint not null,
Name_Image varchar(255),
primary key (id)
)
create table COMPUTERS (
ID_IMAGE bigint not null,
ipAddress varchar(255)
)
alter table COMPUTERS
add constraint FKl1ucm93ttye8p8i9s5cgrurh
foreign key (ID_IMAGE)
references IMAGES
If "manually" declare the join column in the embeddable class, although the DDL are the same, the embeddable object will contain one extra field "imageId", which will cause the JDBC call parameter out of index when executing the INSERT operation.
This is in reference to this answer.
Entities-
// Many to One
#Entity
#Table
public class Address {
#Id
#GeneratedValue
#Column
private int addressIdentity;
#Column
private int houseNo;
#Column
private char streetNo;
#Column
private int pincode;
#Column
private String city;
#Column
private String state;
#Column
private String country;
#ManyToOne
#JoinTable(name="PersonAddress",
joinColumns=#JoinColumn(name="addressId", insertable = false, updatable = false),
inverseJoinColumns=#JoinColumn(name="personId", insertable = false, updatable = false)
)
private Person person;
// getters and setters
One to Many
#Entity
#Table
public class Person {
#Id
#GeneratedValue
#Column
private int personId;
#Column
private String name;
#Column
private String designation;
#OneToMany
#JoinTable(name = "PersonAddress",
joinColumns = #JoinColumn(name = "personId"),
inverseJoinColumns = #JoinColumn(name = "addressId"))
private Set<Address> addSet = new HashSet<Address>();
// getters and setters
Hibernate configuration file-
<hibernate-configuration>
<session-factory name="">
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.password">hello</property>
<property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/xyz</property>
<property name="hibernate.connection.username">postgres</property>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">create</property>
<mapping class="ManyToOne_OneToManyMappingWithJoinTable.Person" />
<mapping class="ManyToOne_OneToManyMappingWithJoinTable.Address" />
</session-factory>
</hibernate-configuration>
the persistence logic-
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
Person person1 = new Person();
person1.setName("Shahnaz Parveen");
person1.setDesignation("HouseWife");
Address address1 = new Address();
address1.setHouseNo(18);
address1.setStreetNo('E');
address1.setPincode(250002);
address1.setCity("Meerut");
address1.setState("UP");
address1.setCountry("INDIA");
address1.setPerson(person1);
Address address2 = new Address();
address2.setHouseNo(84);
address2.setStreetNo('1');
address2.setPincode(250002);
address2.setCity("Meerut");
address2.setState("UP");
address2.setCountry("INDIA");
address1.setPerson(person1);
person1.getAddSet().add(address1);
person1.getAddSet().add(address2);
session.save(address1);
session.save(address2);
session.save(person1);
session.getTransaction().commit();
session.close();
I am getting -
Jan 07, 2017 9:47:35 PM org.hibernate.action.internal.UnresolvedEntityInsertActions logCannotResolveNonNullableTransientDependencies
WARN: HHH000437: Attempting to save one or more entities that have a non-nullable association with an unsaved transient entity. The unsaved transient entity must be saved in an operation prior to saving these dependent entities.
Unsaved transient entity: ([ManyToOne_OneToManyMappingWithJoinTable.Person#0])
Dependent entities: ([[ManyToOne_OneToManyMappingWithJoinTable.Address#1]])
Non-nullable association(s): ([ManyToOne_OneToManyMappingWithJoinTable.Address.person])
Exception in thread "main" org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved beforeQuery current operation : ManyToOne_OneToManyMappingWithJoinTable.Address.person -> ManyToOne_OneToManyMappingWithJoinTable.Person
at org.hibernate.action.internal.UnresolvedEntityInsertActions.checkNoUnresolvedActionsAfterOperation(UnresolvedEntityInsertActions.java:122)
at org.hibernate.engine.spi.ActionQueue.checkNoUnresolvedActionsAfterOperation(ActionQueue.java:418)
at org.hibernate.internal.SessionImpl.checkNoUnresolvedActionsAfterOperation(SessionImpl.java:621)
at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:684)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:674)
at org.hibernate.internal.SessionImpl.save(SessionImpl.java:669)
at ManyToOne_OneToManyMappingWithJoinTable.ManyToOne_OneToManyMappingWithJoinTableImpl.main(ManyToOne_OneToManyMappingWithJoinTableImpl.java:40)
It works perfect with hbms.
Please suggest.
Thanks Vlad and Neil, it works but there is a problem described below-
This is the structure which gets created with HBMs. Hence the same must be with Annotations.
CREATE TABLE person_address
(
addressid integer NOT NULL,
personid integer NOT NULL,
CONSTRAINT person_address_pkey PRIMARY KEY (addressid , personid ),
CONSTRAINT fkkpp6mysmnyiywx3q33yxr1gbe FOREIGN KEY (personid )
REFERENCES person (person_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkrpk0jx2y558su288tx9kd5cs6 FOREIGN KEY (addressid )
REFERENCES address (address_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
the moment I do -
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private Set<Address> addSet = new HashSet<Address>();
the join table structure is-
CREATE TABLE personaddress
(
personid integer,
addressid integer NOT NULL,
CONSTRAINT personaddress_pkey PRIMARY KEY (addressid),
CONSTRAINT fkfd5pm843bldj10y5kxwo37xge FOREIGN KEY (addressid)
REFERENCES address (addressidentity) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkjuwlthwsi53bpf902nnl6snxh FOREIGN KEY (personid)
REFERENCES person (personid) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
You see that the primary key is NOT a combination of addressid and personid as in HBMs. Please suggest.
You need to add a cascade on the one-to-many side:
#OneToMany(cascade = CascadeType.ALL)
Then, change the #ManyToOne side to:
#ManyToOne
#JoinTable(name="PersonAddress",
joinColumns=#JoinColumn(name="addressId"),
inverseJoinColumns=#JoinColumn(name="personId")
)
private Person person;
and the #OneToMany side to:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private Set<Address> addSet = new HashSet<Address>();
To address the composite-key requirement as indicated by the question update, try mapping the join table (e.g. personaddress) as an entity, and use composite identifiers.
I'm creating an application where one large aspect is the ability for users to share content with friends. I'm trying to represent this in the object model and I'm having trouble getting the association to work properly. I'm using a mapping table that records the friender and the friendee, both of which are represented by the primary key (id) of the user. A user can have many friends, and also be referenced by other users. This is what the schema looks like:
Users:
int user_id (PK)
varchar(32) email
varchar(64) password
Users_Map:
int users_map_id (PK)
int friendee_id (FK references users(user_id))
int friender_id (FK references users(user_id))
And this is how I have the User entity set up:
#Data
#Entity
#Table(name = "users")
public class User extends AbstractPersistable<Long> {
#Id
#Column(name = "user_id")
private Long id;
#Column
private String email;
#Column
private String password;
#OneToMany
#JoinTable(name = "users_map",
joinColumns = { #JoinColumn(name = "friender_id") },
inverseJoinColumns = { #JoinColumn(name = "friendee_id") })
private List<User> friends;
}
I run into the following error when deploying the application:
org.hibernate.AnnotationException: A Foreign key refering
com.x.webapp.data.entity.User from
com.x.webapp.data.entity.User has the wrong number of
column. should be 2
I've tried quite a few other configurations, including adding a "referencedColumnName" attribute to each #JoinColumn, but they have also yielded errors. I'm also not entirely sure whether the schema I currently have is the best way to go about mapping users together.
I appreciate any help!
Removing the extension of AbstractPersistable fixed the problem - that contained an #Id reference and clashed with the #Id reference I put inside of User.