Hibernate: How to join attribute with condition using a third table - java

I have the following tables ...
Object1
-------
id
...
Object2
-------
id
...
AttributeValue
--------------
id
attribute_id
object_id
value
Attribute
---------
id
name
type
... and entity classes
#Entity
#Table(name = "Attribute")
public class Attribute {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "type")
private String type;
}
#Entity
#Table(name = "AttributeValue")
public class AttributeValue {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "attribute_id")
private Long attributeId;
#Column(name = "object_id")
private Long objectId;
#Column(name = "value")
private String value;
}
#Entity
#Table(name = "Object1")
public class Object1 {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// ...
// how to annotate to get all matching attribute values?
private Set<AttributeValue> values;
}
I want hibernate to fill the values instance variable with all AttributeValues that have the corresponding object_id and an attribute type of object1.
If it was only about the criterium of object_id, I would write e.g.
#JoinColumn(insertable = false, updatable = false, name = "object_id")
private Set<AttributeValue> values;
But in this case it would fill in also the values with type object2etc.
So my question is: Is this semantic expressible in Hibernate and if so, how?
EDIT: I want to highlight that the goal is to have multiple Objects (here Object1, Object2, ... ObjectN) that have no common hierarchy, but all share the feature of having attributes. The attributes for all objects will reside in one table, distinguished by some sort of discriminator (here exemplarily type).

I think the object must be:
#Entity
#Table(name = "AttributeValue")
public class AttributeValue {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "attribute_id")
private Long attributeId;
#ManyToOne
    #JoinColumn(name="object_id", nullable=false)
private Object1 object1;
#Column(name = "value")
private String value;
}
#Entity
#Table(name = "Object1")
public class Object1 {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy="object1")
private Set<AttributeValue> values;
}
Hibernate will be generate only object_id column on AttributeValue table.

Related

How to add movie_id and user_id in "movie_added_by" table

I have already a user model.
Now I have created a movie model, my requirement is that whenever any existing user is going to add any movie, at that time user_id and movie_id will be store in the movie_added_by table.
Here user model needs to map one to many to movie_added_by and similarly, the movie will be mapped to movie_added_by.
For better understanding, you can refer to the DB diagram.
I really don't know how can I do by using hibernate annotation
The user model is like this:
#Getter
#Setter
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
}
The movie model is like this:
#Getter
#Setter
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
}
You probably want to create a #ManyToMany relationship between the entities. There are 2 ways of doing it (with intermediary table created explicitly or by Hibernate.
In simple approach your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#ManyToMany(cascade = CascadeType.Persist)
#JoinTable(name="user_movie",
joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="movie_id)})
private Set<Movie> movies = new HashSet<>();
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#ManyToMany(cascade = CascadeType.Persist, mappedBy = "movies" //field from the user class responsible for mapping)
private Set<User> users = new HashSet<>()
}
So basically here you tell Hibernate to create an intermediary table and keep there correlated id's of those 2 entities. Couple of other notes here:
a) you might want to change the id variable type from Integer to Long in case your entities grow;
b) If you have annotated a column with #Id, you don't have to use unique=true and nullable = false in the column annotation;
c) remember about implementing no-args constructor;
d) remember to exclude relationship fileds from the equals(), hashCode() and the toString() methods;
There is another way, where you explicitly create a model for the table keeping relationships. This might become handy, when it turns out that You need to keep more data in the 'relationship table'. In that case, Your entities would look as following:
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id", unique = true, nullable = false)
private Integer user_id;
private String name;
#OnetToMany(cascade = CascadeType.PERSIST, mappedBy = "user")
private Set<AddedMovie> addedMovies = new HashSet<>()
}
public class Movie implements Serializable
{
private static final long serialVersionUID = -6790693372846798580L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "movie_id", unique = true, nullable = false)
private Integer movie_id;
private String movie_name;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "movie")
private Set<AddedMovie> moviesAddedByUser = new HashSet<>();
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Entity
public class AddedMovie{
#Id
#GeneratedValue
private Long id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "movie_id")
private Movie movie;
// sine this entity has now its own lifecycle, you can add more fields here
private Integer rating;
private LocalDateTime movieAddedOn;
}

Java JPA how relate an entity instance with all instances of another entity?

I work with an embedded H2 database in which I use the #OneToMany relationship to relate an entity instance (product) to multiple instances of the other entities (suppliers); it's useful when I have specific suppliers for a particular product.
However now, I want to associate all the suppliers with every single product; I don't want to generate in my supplier table different supplier records for each product, instead I want to have only 5 records (5 suppliers) in my supplier table which are associated to every single product, it few words I want to achieve something like "one to all", is it possible to do it using JPA annotations?
Product entity
#Entity
public class Product {
#Id
private String productCode;
#OneToMany
#JoinColumn(name = "supplier_id", referencedColumnName = "productCode")
private List<Supplier> suppliers;
}
Supplier entity
#Entity
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
private String name;
}
Unidirectional #OneToMany association:
#Entity
public class Product {
#Id
// #Column(name = "id") maybe
// #GeneratedValue maybe
private String productCode;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) // according to your need
private List<Supplier> suppliers;
...
}
And,
#Entity
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
...
}
#ManyToOne association:
#Entity
public class Product {
#Id
// #Column(name = "id") maybe
// #GeneratedValue maybe
private String productCode;
...
}
And,
#Entity
public class Supplier {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "product_id", foreignKey = #ForeignKey(name = "PRODUCT_ID_FK"))
private Product product;
private String name;
...
}

Specific kind of relation in hibernate

For example, I've got a table A with structure:
int id | int ref_id | varchar name
0 - hello
1 0 world
And entity class:
#Entity
#Table(name = "mypack.A")
public class A
{
#Id
#Column(name = "ID")
private int id;
#Column(name = "REF_ID", nullable=true)
private int ref_id;
#Column(name = "NAME")
private String name;
// getters and setters
}
Row with id 1 refers to row with id 0. How can I do this kind of relation using Hibernate? I have an idea to create A class object inside that. Is it ok?
You can use instead of the property ref_id of type int use a reference to another A object.
#Entity
#Table(name = "mypack.A")
public class A implements Serializable {
#Id
#GeneratedValue
#Column(name = "ID")
private Long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "REF_ID")
private A refA;
#Column(name = "NAME")
private String name;
// getters and setters
}

Hibernate: Inconsistent results with #OneToOne unidirectional relationships

I'm getting inconsistent results with #OneToOne unidirectional relationships in Hibernate. Specifically, I have a User table with a couple of relationships to other tables: Foo and Bar.
Foo is mapped to User by the user's uuid property (a String). Bar is mapped to User by the user's id property (an Integer).
My entities look something like this:
#Entity
#Table(name = "User")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#Column(name = "uuid")
private String uuid;
#OneToOne
#JoinColumn(name = "uuid", referencedColumnName = "frn_object_uuid")
private Foo foo;
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "frn_user_id")
private Bar bar;
// getters and setters
}
#Entity
#Table(name = "Foo")
public class Foo {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#Column(name = "frn_object_uuid")
private String objectUuid;
#Column(name = "foo")
private String foo;
// getters and setters
}
#Entity
#Table(name = "Bar")
public class Bar {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
#Column(name = "frn_user_id")
private Integer userId;
#Column(name = "bar")
private String bar;
// getters and setters
}
When I perform a read on the User object, I get results for Foo but NULL for Bar.
What am I missing here? I'm able to get results if I make Bar a bidirectional relationship using mappedBy...very strange.
Thanks in advance!

Multi-Column Join in Hibernate/JPA Annotations

I have two entities which I would like to join through multiple columns. These columns are shared by an #Embeddable object that is shared by both entities. In the example below, Foo can have only one Bar but Bar can have multiple Foos (where AnEmbeddableObject is a unique key for Bar). Here is an example:
#Entity
#Table(name = "foo")
public class Foo {
#Id
#Column(name = "id")
#GeneratedValue(generator = "seqGen")
#SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1)
private Long id;
#Embedded
private AnEmbeddableObject anEmbeddableObject;
#ManyToOne(targetEntity = Bar.class, fetch = FetchType.LAZY)
#JoinColumns( {
#JoinColumn(name = "column_1", referencedColumnName = "column_1"),
#JoinColumn(name = "column_2", referencedColumnName = "column_2"),
#JoinColumn(name = "column_3", referencedColumnName = "column_3"),
#JoinColumn(name = "column_4", referencedColumnName = "column_4")
})
private Bar bar;
// ... rest of class
}
And the Bar class:
#Entity
#Table(name = "bar")
public class Bar {
#Id
#Column(name = "id")
#GeneratedValue(generator = "seqGen")
#SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1)
private Long id;
#Embedded
private AnEmbeddableObject anEmbeddableObject;
// ... rest of class
}
Finally the AnEmbeddedObject class:
#Embeddable
public class AnEmbeddedObject {
#Column(name = "column_1")
private Long column1;
#Column(name = "column_2")
private Long column2;
#Column(name = "column_3")
private Long column3;
#Column(name = "column_4")
private Long column4;
// ... rest of class
}
Obviously the schema is poorly normalised, it is a restriction that AnEmbeddedObject's fields are repeated in each table.
The problem I have is that I receive this error when I try to start up Hibernate:
org.hibernate.AnnotationException: referencedColumnNames(column_1, column_2, column_3, column_4) of Foo.bar referencing Bar not mapped to a single property
I have tried marking the JoinColumns are not insertable and updatable, but with no luck. Is there a way to express this with Hibernate/JPA annotations?
This worked for me . In my case 2 tables foo and boo have to be joined based on 3 different columns.Please note in my case ,in boo the 3 common columns are not primary key
i.e., one to one mapping based on 3 different columns
#Entity
#Table(name = "foo")
public class foo implements Serializable
{
#Column(name="foocol1")
private String foocol1;
//add getter setter
#Column(name="foocol2")
private String foocol2;
//add getter setter
#Column(name="foocol3")
private String foocol3;
//add getter setter
private Boo boo;
private int id;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "brsitem_id", updatable = false)
public int getId()
{
return this.id;
}
public void setId(int id)
{
this.id = id;
}
#OneToOne
#JoinColumns(
{
#JoinColumn(updatable=false,insertable=false, name="foocol1", referencedColumnName="boocol1"),
#JoinColumn(updatable=false,insertable=false, name="foocol2", referencedColumnName="boocol2"),
#JoinColumn(updatable=false,insertable=false, name="foocol3", referencedColumnName="boocol3")
}
)
public Boo getBoo()
{
return boo;
}
public void setBoo(Boo boo)
{
this.boo = boo;
}
}
#Entity
#Table(name = "boo")
public class Boo implements Serializable
{
private int id;
#Column(name="boocol1")
private String boocol1;
//add getter setter
#Column(name="boocol2")
private String boocol2;
//add getter setter
#Column(name="boocol3")
private String boocol3;
//add getter setter
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id", updatable = false)
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
}
If this doesn't work I'm out of ideas. This way you get the 4 columns in both tables (as Bar owns them and Foo uses them to reference Bar) and the generated IDs in both entities. The set of 4 columns has to be unique in Bar so the many-to-one relation doesn't become a many-to-many.
#Embeddable
public class AnEmbeddedObject
{
#Column(name = "column_1")
private Long column1;
#Column(name = "column_2")
private Long column2;
#Column(name = "column_3")
private Long column3;
#Column(name = "column_4")
private Long column4;
}
#Entity
public class Foo
{
#Id
#Column(name = "id")
#GeneratedValue(generator = "seqGen")
#SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "column_1", referencedColumnName = "column_1"),
#JoinColumn(name = "column_2", referencedColumnName = "column_2"),
#JoinColumn(name = "column_3", referencedColumnName = "column_3"),
#JoinColumn(name = "column_4", referencedColumnName = "column_4")
})
private Bar bar;
}
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {
"column_1",
"column_2",
"column_3",
"column_4"
}))
public class Bar
{
#Id
#Column(name = "id")
#GeneratedValue(generator = "seqGen")
#SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1)
private Long id;
#Embedded
private AnEmbeddedObject anEmbeddedObject;
}
Hibernate is not going to make it easy for you to do what you are trying to do. From the Hibernate documentation:
Note that when using referencedColumnName to a non primary key column, the associated class has to be Serializable. Also note that the referencedColumnName to a non primary key column has to be mapped to a property having a single column (other cases might not work). (emphasis added)
So if you are unwilling to make AnEmbeddableObject the Identifier for Bar then Hibernate is not going to lazily, automatically retrieve Bar for you. You can, of course, still use HQL to write queries that join on AnEmbeddableObject, but you lose automatic fetching and life cycle maintenance if you insist on using a multi-column non-primary key for Bar.

Categories

Resources