I know this makes none sense as many tutorials state that you can use SecondaryTable annotation, however it doesn't work in hibernate. I have schema like this:
#Entity
#Table(name="server")
#SecondaryTable(name="cluster", pkJoinColumns = { #PrimaryKeyJoinColumn(name = "uuid", referencedColumnName = "cluster_uuid") })
public class Server {
#Id
#Column(name = "uuid")
private String uuid;
#Column(name = "cluster_uuid")
private String clusterUuid;
#Column(name = "ip", table="cluster")
private String ip;
..... }
#Entity
#Table(name = "cluster")
public class Cluster {
#Id
#Column(name = "uuid")
private String uuid;
#Column(name = "ip")
private String ip;
.....
}
Server.clusterUuid is a foreign key to Cluster.uuid. I am hoping to get Server entity that fetches ip column from Cluster by joining Server.clusterUuid to Cluster.uuid.
Then I was greeted by a hibernate exception:
Caused by: org.hibernate.AnnotationException: SecondaryTable
JoinColumn cannot reference a non primary key
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:402)
at org.hibernate.cfg.annotations.EntityBinder.bindJoinToPersistentClass(EntityBinder.java:620)
at org.hibernate.cfg.annotations.EntityBinder.createPrimaryColumnsToSecondaryTable(EntityBinder.java:612)
I see lots of people encountered this problem. But the first bug for this in Hibernate's bugzilla was 2010, I am surprised it's been there for over two years as this is supposed to be a basic feature. There is some post saying JPA spec only allows primary key to do the mapping, however, I get below from JPA wikibook
JPA allows multiple tables to be assigned to a single class. The
#SecondaryTable and SecondaryTables annotations or
elements can be used. By default the #Id column(s) are assumed to be
in both tables, such that the secondary table's #Id column(s) are the
primary key of the table and a foreign key to the first table. If
the first table's #Id column(s) are not named the same the
#PrimaryKeyJoinColumn or can be used to
define the foreign key join condition.
it's obviously OK for non-primary key. Then I am confused why Hibernate didn't fix this problem as it seems to be easy to implement by a join clause.
anybody knows how to overcome this problem? thank you.
I don't quite understand your setup.
#SecondaryTable is for storing a single entity in multiple tables, but in your case you have a many-to-one relationship between different entities (each one stored in its own table), and it should be mapped as such:
#Entity
#Table(name="server")
public class Server {
#ManyToOne
#JoinColumn(name = "cluster_uuid")
private Cluster cluster;
...
}
Related
I have the following spring-data-jpa entity:
#Entity(name = "absenceDays")
#Table
public final class MyTable {
#EmbeddedId
private MyId myId;
#Column(nullable = false)
private Long anotherId;
}
further, this is the #Embeddable entity used above:
public final class MyId implements Serializable {
#Column(updatable = false, nullable = false)
private Long id;
#Column(updatable = false, nullable = false)
private LocalDate date;
}
I have couple of questions?
Are tables already indexed with their primary keys? It seems to be implementation specific, as discussed here When should I use primary key or index?
How should I index my table with the composite id using the JPA 2.1 #Index annotation, if I need to index my table?
My DB of choice will be AWS RDS with MySQL InnoDB dialect.
Since you tagged the Question [mysql], I will address it from that point of view.
In MySQL the PRIMARY KEY is always UNIQUE and a KEY (aka INDEX). In the case of ENGINE=InnoDB, it is also "clustered" with the data. This makes fetching a row, given the PK, very fast.
To ask questions related to MySQL, it is best to dig below the 3rd party interface (Spring, in your case) to get to the MySQL (or MariaDB or Aurora) info, such as CREATE TABLE and SELECT....
I could probably answer your Q2 with the above info.
Am using springboot with hibernate,
My Entity classes looks like below :
#Entity
#Table(name="tbl_user")
public class User {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="user_Id")
private long userId;
#Column(name="userName")
private String userName;
#Column(name="passWord")
private String passWord;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name="user_role")
private Collection<Role> roleList;
My second entity looks like below :
#Entity
#Table(name="tbl_role")
public class Role {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="role_Id")
private long roleId;
#Column(name="roleName")
private String roleName;
When I insert first user with role as manager(pkid=1), admin(pkid=2) its success but while I tried to insert 2nd user with role as Manager*pkid=1, admin(pkid=2, serviceUser(pkid=3) it's not allowing me to insert second user with below exception
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2' for key 'UK_ixctfj5iq0enl7iktlpo7wxct'
Can somebody help me why this constraint is getting creating while generating tables, how can i insert 2nd user into DB ?
If you use OnetoMany on role_list you are effectively saying that a single User will point to many Roles and that a Role will point to only one User. This will be enforced with a unique key constraint placed on the join table. If you have the SQL statements printed out you will see it when the schema is created. Something along the lines of:
alter table user_role add constraint UK_ixctfj5iq0enl7iktlpo7wxct unique (role_id)
In your requirement, you also have a single Role used by many Users. Your admin role primary key is 2 and you want to be able to assign it to more than one user. Your relationship is a ManyToMany for the role_list.
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="user_role")
private Collection<Role> roleList;
When you change the annotation, you will still have a join table, but no constraint will be added.
I’m using MySQL 5.5.37, JPA 2.0, and Hibernate 4.1.0.Final (I’m willing to upgrade if it solves my problem). I have the following entity …
#Entity
#Table(name = "url")
public class Url implements Serializable
{
…
#ElementCollection(fetch=FetchType.EAGER)
#MapKeyColumn(name="property_name")
#Column(name="property_value")
#CollectionTable(name="url_property", joinColumns=#JoinColumn(name="url_id"))
private Map<String,String> properties;
The “url_property” table has an ID (primary key) column, and perhaps for this reason, when I create a new Url entity with multiple properties, I feet the exception
[ERROR]: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry '' for key 'PRIMARY'
upon saving. Does anyone know what I have to do to auto-generate IDs for my url_property table? I would prefer not to write a trigger, but rather do something JPA, or at least, Hibernate sanctioned.
Edit: Per the first suggestion in the answer, I tried
#ElementCollection(fetch=FetchType.EAGER)
#Column(name="property_value")
#CollectionTable(name="url_property", joinColumns=#JoinColumn(name="url_id"))
private Set<UrlProperty> properties;
but it resulted in the exception, "org.hibernate.MappingException: Foreign key (FK24E4A95BB0648B:url_property [properties_id])) must have same number of columns as the referenced primary key (url_property [url_id,properties_id])".
My UrlProperty entity is
#Entity
#Table(name = "url_property")
public class UrlProperty
{
#Id
#GeneratedValue(generator = "uuid-strategy")
private String id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="URL_ID")
private SubdomainUrl url;
#Column(name="PROPERTY_NAME")
private String propertyName;
#Column(name="PROPERTY_VALUE")
private String propertyValue;
You have only told JPA about 3 fields in the table ("property_name","property_value" and "url_id"), so it has no way of knowing about the 4th field used as the pk. Since it is not an entity, it doesn't have an Identity that is maintained. Options are:
1) Map the "url_property" table to a Property entity, which would have an ID, value and reference to the Url. The Url would then have a 1:M reference to the Property class, and can still be keyed on the name. http://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/MapKeyColumns has an example
2) Change your table to remove the ID field, and instead use "property_name","property_value" and "url_id" as the primary key.
3) Set a trigger to populate the ID. Doesn't seem useful though since the application is never aware of the field anyway.
I am confused about how the JPA handles a 1:1 unidirectional relationship when I auto generate my tables from my entity classes vs. how I would make those tables if I was creating the SQL tables myself.
My question concerns how the foreign keys are set up.
Assuming I have a Customer entity and an Address entity like this:
#Entity
public class Customer{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(cascade = CascadeType.ALL)
private Address address;
//setters and getters
}
#Entity
public class Address{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String streetName;
//setters and getters
}
If I generate the sql tables from these two entity classes then the Customer table will have a foreign key column referencing the Address table.
Is there a way to have a OneToOne unidirectional relationship where the JPA sticks the foreign key on the target table(the address table) instead of on the Customer table as it does in my code example, or do I have to make it a bidirectional relationship and show more explicit ownership with the #Mapping annotation to achieve this kind of table structure?
To have the foreign key appear on the other table for a 1-1 unidirectional relationship you would need to switch the 'owning' side: place customer in Address and remove address from Customer. Then the foreign key for customer would be in the address table.
First of all, you have to map your address_id(or something like that) from the Customer entity. For example using below code, you are referencing the Address from the Customer.
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="ADDRESS_ID")
private Address address;
While deleting any entry from the address table, you must first delete the entry from the Customer table in order to avoid the foreign key violation error. Then you can delete from Address table. JPA doesn't handle this relationship while deleting any row from table.
I have been searching through different forums for information and I have tried different solutions, but I'm still unable to correct my issue.
I am using hibernate4 annotations for mapping my entities. Auto increment key is not detected when tables are created using hibernate in mysql.
I have the following code:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private int responseId;
I have also tried:
#Id
#GenericGenerator(name="generator", strategy="increment")
#GeneratedValue(generator="generator")
private int responseId;
With hibernate the id is automatically assigned to a row, but in the mysql table it has no AutoIncrement Constraint. I have to mark field as AI manually. This becomes problematic when I manually insert a record for testing or use jdbc statements for the table. Please let me know what I am missing in configuration that is preventing the hibernate id from assigning an AutoIncrement Contraint.
Use the IDENTITY generator, and use the columnDefinition attribute of #Column to specify the type of the column:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(columnDefinition = "MEDIUMINT NOT NULL AUTO_INCREMENT")
private int responseId;