list only has one element - java

I'm using hibernate to read data from a database. I have 3 entities, sensor, zone and a N to N relationship between those two entities, that is the entity SensorZone.
For each entity I created a class, which are the examples that follow, they don't include a constructor, getters and setters:
#Entity
#Table(name = "Sensor")
public class Sensor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "zone")
private List<SensorZone> zones = new ArrayList<>();
}
#Entity
#Table(name = "Zone")
public class Zone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#Column(name = "max_count")
private int maxCount;
#Column(name = "current_count")
private int currentCount;
#Column(name = "created")
private Timestamp created;
#Column(name = "modified")
private Timestamp modified;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "sensor")
private List<SensorZone> sensors = new ArrayList<>();
}
#Entity
#Table(name = "Sensor_Zone")
public class SensorZone {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "zone_id")
private Zone zone;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "sensor_id")
private Sensor sensor;
#Column(name = "enter_exit")
private boolean enterExit;
}
I'm using PostgreSQL as a database engine, and the tables are as follow:
CREATE TABLE Sensor (
id SERIAL,
name TEXT NOT NULL UNIQUE,
CONSTRAINT PK_Sensor PRIMARY KEY (id)
);
CREATE TABLE Zone (
id SERIAL,
name TEXT NOT NULL UNIQUE,
max_count INT NOT NULL,
current_count INT NOT NULL,
created TIMESTAMP NOT NULL,
modified TIMESTAMP NOT NULL,
CONSTRAINT PK_Zone PRIMARY KEY (id)
);
CREATE TABLE Sensor_Zone (
id SERIAL,
zone_id INT NOT NULL,
sensor_id INT NOT NULL,
enter_exit BOOLEAN NOT NULL,
CONSTRAINT PK_Zone_Sensor PRIMARY KEY (id, sensor_id, zone_id),
CONSTRAINT FK_Zone_Sensor_Zone FOREIGN KEY (zone_id) REFERENCES Zone (id),
CONSTRAINT FK_Zone_Sensor_Sensor FOREIGN KEY (sensor_id) REFERENCES Sensor (id)
);
And here's the values on the table Sensor_Zone:
The problem is that the field zones from Sensor only has one element in the list, even there are multiple elements in the database.
I've tried to put FecthType.EAGER, but it didn't change anything.

Not the exact solution (a.k.a. Could your model be re-modeled?)
I know it's easier said than done, but if you could avoid managing extra column(s) on the joining table, you could resolve it using standard example of many-to-many with joining table.
Perhaps there is a way of thinking about your extra column so that it could become owned by either Zone or Sensor entities? Maybe it's complementary as in on one side of many-to-many if it is set to TRUE it means one thing, but when it's missing it is equivalent to being FALSE.
#Entity
#Table(name = "Sensor")
public class Sensor {
// ...
#OneToMany
#JoinTable(
name = "Sensor_Zone",
joinColumns = { #JoinColumn(name = "sensor_id") },
inverseJoinColumns = { #JoinColumn(name = "zone_id") }
)
private List<Zone> zones = new ArrayList<>();
}
#Entity
#Table(name = "Zone")
public class Zone {
// ...
#OneToMany
#JoinTable(
name = "Sensor_Zone",
joinColumns = { #JoinColumn(name = "zone_id") },
inverseJoinColumns = { #JoinColumn(name = "sensor_id") }
)
private List<Sensor> sensors = new ArrayList<>();
}
And then your joining table does not need to be an entity simplifying things.
The Solution (Here be dragons)
Escape hatch: for solution please check Mykong's blog
You have to look into using #AssociationOverrides on the entity created from joining table. Please also note a separate class annotated with #Embeddable created to deal with the composite key in the joining table.

Related

How to retrieve ONE column from another table with Foreign key using Hibernate / JPA

I would like to use the Foreign key "MODEL_ID" to retrieve just one column "MODEL_NAME" from the TT_CARS table,
I tried the following code, that works but it returns the whole CARS object.
#JoinColumn(name = "MODEL_ID", referencedColumnName = "ID")
#ManyToOne(fetch = FetchType.EAGER)
private CARS cars;
Also I tried the code below, its also not working
#SecondaryTable(name = "TT_CARS", pkJoinColumns = #PrimaryKeyJoinColumn(name = "ID", referencedColumnName="MODEL_ID"))
Is there other way to retieve just the column (MODEL_NAME) using hibernate and JPA??
remarks: The modelName should be part of the Options class.
my code
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
#Entity
#Table(name = "TT_OPTIONS")
public class Options {
#Id
#Column(name = "ID")
private String id;
#NotNull
#Column(name = "DESCRIPTION", nullable = false)
private String description;
#Column(name = "MODEL_ID") // Foreign key
private Long modelId;
#Column(name = "MODEL_NAME", table = "TT_CARS") // this is the column name I would like to retrieve from the TT_CARS table
private String modelName;
// getters and setters
}
You can use #Formula. It is read-only calculated column that can be retrieved by the custom subquery. It does not present in the target table.
Defines a formula (derived value) which is a SQL fragment that acts as
a #Column alternative in most cases. Represents read-only state.
Example:
#Entity
#Table(name = "TT_OPTIONS")
public class Options {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "DESCRIPTION", nullable = false)
private String description;
#Column(name = "MODEL_ID")
private Long modelId;
#Formula("(select TT_CARS.MODEL_NAME from TT_CARS where TT_CARS.ID = MODEL_ID)")
private String modelNameFormula;
}
#Entity
#Table(name = "TT_CARS")
public class Cars {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "MODEL_NAME")
private String modelName;
}
Hibernate generated native query:
select
options0_.id as id1_4_0_,
options0_.description as descript2_4_0_,
options0_.model_id as model_id3_4_0_,
(select
TT_CARS.MODEL_NAME
from
TT_CARS
where
TT_CARS.ID = options0_.MODEL_ID) as formula1_0_
from
tt_options options0_
where
options0_.id=?
#SecondaryTable designed for #OneToOne relationship to map multiple tables to the same entity. It will not work for the #ManyToOne relationship.

Spring Boot Jpa relationship with part of Embeded Id

I am creating entity relationships in Spring Boot data JPA. Since those tables being legacy I am not able to modify or add columns. Issue is I am getting error if point part of embedded Id.
My entity classes looks like below:
Class Customer {
#EmbededId
private CustomerPk id;
#Column("NAME")
private String name;
#OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL, mappedBy="customerDetails")
private List<Purchase> purchaseDetails;
...
}
#Embeddable
Class CustomerPk {
#Column("CUSTOMER_ID")
private String customerId
#Column("PURCHASE_ID")
private String productId;
#Column("PURCHASE_DATE")
private String date;
}
Purchase Entity looks like below:
Class Purchase {
#EmbededId
private PurchasePK id;
#Column("TRANSACTION_NAME")
private String transactionName;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumns({
#JoinColumn(name="CUSTOMER_ID" referencedColumnName="CUSTOMER_ID")
#JoinColumn(name="PURCHASE_ID" referencedColumnName="PURCHASE_ID")
)}
private Customer customerDetails;
...
}
#Embeddable
Class PurchasePK {
#Column("CUSTOMER_ID")
private String customerId
#Column("PURCHASE_ID")
private String productId;
#Column("TRANSACTION_DATE")
private String date;
}
With above structure I am getting org.hibernate.AnnotationException: referencedColumnNames(CUSTOMER_ID, PURCHASE_ID) of Purchase.customerDetails referencing Customer not mapped to a single property.
If I remove date property from CustomerPK, I am able to make the server up. But with current requirement I need date to be part of the CustomerPK class.
I think if I use part of the composite key as Join Columns I am getting this error.
Working version:
#Entity
public class Customer {
#EmbeddedId
private CustomerPk id;
#Column(name = "NAME")
private String name;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "customerDetails")
private List<Purchase> purchaseDetails;
}
#Embeddable
public class CustomerPk implements Serializable {
#Column(name = "CUSTOMER_ID")
private String customerId;
#Column(name = "PURCHASE_ID")
private String productId;
#Column(name = "PURCHASE_DATE")
private String date;
}
#Entity
public class Purchase {
#EmbeddedId
private PurchasePK id;
#Column(name = "TRANSACTION_NAME")
private String transactionName;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID", insertable = false, updatable = false),
#JoinColumn(name = "PURCHASE_ID", referencedColumnName = "PURCHASE_ID", insertable = false, updatable = false),
#JoinColumn(name = "PURCHASE_DATE", referencedColumnName = "PURCHASE_DATE", insertable = false, updatable = false)
})
private Customer customerDetails;
}
#Embeddable
public class PurchasePK implements Serializable {
#Column(name = "CUSTOMER_ID")
private String customerId;
#Column(name = "PURCHASE_ID")
private String productId;
#Column(name = "TRANSACTION_DATE")
private String date;
}
Conclusion:
The provided information from #Ray was valid, you missed adding the required join columns to represent the full entity relation, regarding your note for the same #Ray point, yes you are right both columns usage is different but also both columns have their own name which it will not override any row value on runtime.
The result of the above tables and representation is as follows:
create table customer
(
customer_id varchar(255) not null,
purchase_date varchar(255) not null,
purchase_id varchar(255) not null,
name varchar(255),
primary key (customer_id, purchase_date, purchase_id)
);
create table purchase
(
customer_id varchar(255) not null,
transaction_date varchar(255) not null,
purchase_id varchar(255) not null,
transaction_name varchar(255),
purchase_date varchar(255),
primary key (customer_id, transaction_date, purchase_id)
);
alter table purchase
add constraint FK6rkrb8rq8x56kai7g5gm32d1y foreign key (customer_id, purchase_date, purchase_id) references customer;

Java: Repeated column in mapping for collection

I am trying to create a OneToMany mapping with a LinkedHashMap for my main entity B, that contains the entities VC and P, but I am getting the following error:
Repeated column in mapping for collection:
com.test.model.B.pricing column: b_name
I could be wrong, but I believe that it has something to do with the #JoinColumns or #MapKeyJoinColumn annotation, as I have not done anything like this before, so I am quite sure that I am doing this part incorrectly.
My goal is that I should be able to provide the three fields:
b_name pc and c_id e.g. the VC/VCId
in order to get the a and d_a e.g. P.
Also, if there is a better way to structure things, then I am all ears, as I personally do not really like how I have set up my tables tbh (would be nice if I could just have the b and b_p tables, where the b_p could just have all five fields (key and value) from the p map).
Here is my main entity
#Setter
#Getter
#Entity
#Table(name = "b")
public class B implements Serializable {
#Id
#Column(nullable = false)
private String name;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name = "b_p",
joinColumns = #JoinColumn(name = "b_name", referencedColumnName = "name"))
#MapKeyJoinColumns({
#MapKeyJoinColumn(name = "b_name"),
#MapKeyJoinColumn(name = "p_c"),
#MapKeyJoinColumn(name = "c_id")
})
private Map<VC, Price> pricing = new LinkedHashMap<>();
...
}
The Key to the map
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "v_c")
public class VC implements Serializable {
#EmbeddedId private VCId vcId;
}
The key's PK/Composite Key
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Embeddable
public class VCId implements Serializable {
#Column(name = "b_name")
private String bName;
#Column(name = "p_c")
private SomeEnum pc;
#Column(name = "c_id")
private String cId;
}
The value for the map
#Setter
#Getter
#NoArgsConstructor
#Embeddable
#Entity
#Table(name = "price")
public class Price implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "amount")
private BigDecimal amount;
#Column(name = "discount_amount")
private BigDecimal discountAmount;
}
DB tables
CREATE TABLE b
(
name VARCHAR(100) NOT NULL PRIMARY KEY
...
);
CREATE TABLE v_c
(
bundle_name VARCHAR(100) NOT NULL,
physical_currency TEXT NOT NULL,
coin_id VARCHAR(50) NOT NULL,
FOREIGN KEY (b_name) REFERENCES b (name) ON DELETE CASCADE,
PRIMARY KEY (b_name, p_c, c_id)
);
CREATE TABLE p
(
id BIGSERIAL NOT NULL PRIMARY KEY,
amount NUMERIC,
discount_amount NUMERIC DEFAULT 0.00
);
CREATE TABLE b_p
(
bname VARCHAR(100) NOT NULL,
p_c TEXT NOT NULL,
c_id VARCHAR(50) NOT NULL,
price_id BIGSERIAL NOT NULL,
FOREIGN KEY (b_name, p_c, c_id) REFERENCES v_c (b_name, p_c, c_id) ON DELETE CASCADE,
FOREIGN KEY (price_id) REFERENCES price (id) ON DELETE CASCADE,
PRIMARY KEY (b_name, p_c, c_id)
);
To prevent repeated mapping error, you just have to specify which join should update the column like such:
#OneToMany(cascade = CascadeType.PERSIST)
#JoinTable(name = "bundle_pricing",
joinColumns = #JoinColumn(name = "bundle_name",
referencedColumnName = "name"))
#MapKeyJoinColumns({
#MapKeyJoinColumn(name = "bundle_name", insertable = false, updatable = false),
#MapKeyJoinColumn(name = "physical_currency"), #MapKeyJoinColumn(name = "coin_id")})
private Map<VirtualCurrency, Price> pricing = new LinkedHashMap<>();
NOTE the insertable = false, updatable = false for bundle_name in the MapKeyJoinColumn
remove the #MapKeyJoinColumn(name = "bundle_name") from MapKeyJoinColumns, because when we are creating the JoinColumn in JoinTable it will create the column we don't need to mention it again.
#JoinTable(
name = "bundle_pricing",
joinColumns = #JoinColumn(name = "bundle_name", referencedColumnName = "name"))
#MapKeyJoinColumns({
#MapKeyJoinColumn(name = "physical_currency"),
#MapKeyJoinColumn(name = "coin_id")
})

Hibernate create foreign key to itself

I have a very strange problem with hibernate at the moment.
Somehow on a table, it create a foreign key which reference to itself. the column is also a primary key. This essentially prevent me from delete any row from that table.
In the log I could see a line:
DEBUG org.hibernate.SQL - alter table Device add index
FK79D00A76C682495E (id), add constraint FK79D00A76C682495E foreign key
(id) references Device (id)
Other table with similar table seems fine. and this seems to be true for both MySQL and Derby. I'm using hibernate 4.1.4
the annotated class is as follow.
#Entity(name = "Device")
public class Device extends DomainObject implements Searchable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
#Column(name = "Type")
#Enumerated(EnumType.STRING)
private DeviceTypeEnum type = DeviceTypeEnum.AccessControlDevice;
#Column(name = "Name", length = Constance.DATABASE_NAME_FIELD_LENGTH)
private String name;
#Column(name = "Description", length = Constance.DATABASE_DESCRIPTION_FIELD_LENGTH)
private String description;
#Column(name = "Identifier", length = Constance.DATABASE_IDENTIFIER_FIELD_LENGTH, unique = true)
private String identifier;
#ManyToMany
#JoinTable(name = "Device2Group", joinColumns = {#JoinColumn(name = "DeviceID")}, inverseJoinColumns = {#JoinColumn(name = "DeviceGroupID")})
private List<DeviceGroup> groups = new ArrayList<DeviceGroup>();
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "location")
private Location location;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "Address")
private Address address;
#ManyToOne
#JoinColumn(name = "Link", nullable = false)
private Link link;
}
It turns out that in one of the Entity Class "Location" which the Device entity references, it has a #ManyToMany mapped collection of Device, where it really should be #OneToMany.
After change the #ManyToMany to #OneToMany the constrain disappears.
I see no references to Device class in your code. So I am assuming that this class has been modified, but its table has not, because it has some data. (Why else should it have a foreign-key to itself?)
Try dropping this table in your database to make hibernate create it once more, or set p.hibernate.hbm2ddl.auto to create-drop.

Three-way hibernate ORM mapping - how to?

I have a three SQL tables:
create table users (
id serial primary key,
name text not null unique
);
create table posts (
id serial primary key,
data text not null,
authorId integer not null references users (id)
);
create table ratings (
id serial primary key,
name text not null unique
);
One post can have only one author, so users <-> posts relation is already established in normal form (correct me if i am wrong).
Ratings are pre-defined constants like "bad", "good" or "awesome", with (in real case) additional data as rating value, description or other fields i omitted here for brevity.
Next i want to relate ratings to users and posts. Each post may be rated once by each user, and may be rated by multiple users. I came up with the following relation:
create table posts_ratings_users_map (
postId integer not null references posts (id),
ratingId integer not null references ratings (id),
userId integer not null references users (id),
primary key (postId, ratingId, userId)
);
But here is the problem: i can't see a way to integrate it within Hibernate ORM mapping, to get for each of posts list (or set, or any other collection) of pairs of (User,Rating).
Here is how i am trying to map them now:
User.java:
#Entity(name = "users")
public class User {
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
#OneToMany
#JoinColumn(name = "authorId")
private Set<Post> posts = new HashSet<>();
// ...
// getters and setters
// ...
}
Rating.java:
#Entity(name = "ratings")
public class Rating {
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
#ManyToMany(mappedBy = "ratings")
private Set<Post> posts = new HashSet<>();
// ...
// getters and setters
// ...
}
Post.java:
#Entity(name = "posts")
public class Post {
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String data;
#ManyToOne
#JoinColumn(name = "authorId")
private User author;
#ManyToMany
#JoinTable(
name = "posts_ratings_users_map",
joinColumns = { #JoinColumn(name = "ratingId") },
inverseJoinColumns = { #JoinColumn(name = "postId") }
)
private Set<Rating> ratings = new HashSet<>(); // here is the problem. i can relate ratings to this post, but how
// do i relate them with users which assigned their ratings to this
// post ?
// ...
// getters and setters
// ...
}
What needs to be changed in order to relate list of pairs of rating&user to each post?
UPD1
Obvious error: PK for posts_ratings_users_map should be (postId, userId) (excluding ratingId), otherwise same user was able to put different ratings on the same post.
Maybe change your model a little.
Users have posts which you said is already defined.
Why not create a new Entity 'UserRating'.
#Entity(name = "user_ratings")
public class UserRating {
#Id
#Column(nullable = false)
#GeneratedValue
private Integer id;
#ManyToOne(nullable = false)
#JoinColumn(name = "ratingId")
private Rating rating;
#ManyToOne(nullable = false)
#JoinColumn(name = "authorId")
private User ratedBy;
}
Now on your Post instead of the ManyToMany have a OneToMany relationship. It would be more ideal to use the id of the rating and the user as the key for the UserRating class but this doesnt model easily in JPA/Hibernate which makes it quite complex. If your interested have a look at these questions
Mapping ManyToMany with composite Primary key and Annotation
Many to many hibernate mapping with extra columns?
Include the user in the rating class
#Entity(name = "ratings")
public class Rating {
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
#ManyToMany(mappedBy = "ratings")
private Set<Post> posts = new HashSet<>();
#Onetoone
private User ratedBy;
// ...
// getters and setters
// ...
}

Categories

Resources