I have been trying to map some "OneToOne" relationships between two users via an intermediate class called Guardian. When i try to retrieve a user (and his guardians) i get an internal server error in return from Glassfish (Open edition v4.0). There is however no stack trace of any kind or any error displayed in the logs. I suspect that the issue is my mapping within the JPA classes.
Starting the server i get two warnings related to the Guardian class which I don't really understand:
WARNING: The reference column name [GUARDIAN] mapped on the element [method getGuardianUserBean] does not correspond to a valid id or basic field/column on the mapping reference. Will use referenced column name as provided.
WARNING: The reference column name [OWNER] mapped on the element [method getOwnerUserBean] does not correspond to a valid id or basic field/column on the mapping reference. Will use referenced column name as provided.
SQL create statements:
create table HOMEFREE."user" (
userid integer GENERATED ALWAYS AS IDENTITY,
name varchar(255) not null,
displayname varchar(255) unique not null,
password varchar(255) not null,
tlf integer,
facebookID varchar(255),
googleid varchar(255),
authtoken varchar(255),
email varchar(255) unique not null,
primary key(userid)
);
create table HOMEFREE."guardian" (
guardianId integer GENERATED ALWAYS AS IDENTITY,
owner integer not null,
guardian integer not null,
confirmed boolean not null,
primary key(guardianId),
foreign key(owner) references homeFree."user"(userid),
foreign key(guardian) references homeFree."user"(userid)
);
Relevant fields/annotations in entity classes:
#Entity
#Table(name = "\"user\"", schema = "HOMEFREE")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int userId;
#OneToMany(mappedBy = "ownerUserBean", fetch = FetchType.EAGER)
private List<Guardian> guardians;
}
#Entity
#Table(name="\"guardian\"", schema="HOMEFREE")
public class Guardian implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int guardianId;
#OneToOne
#PrimaryKeyJoinColumn(name="OWNER", referencedColumnName="USERID")
private User ownerUserBean;
#OneToOne
#PrimaryKeyJoinColumn(name="GUARDIAN", referencedColumnName="USERID")
private User guardianUserBean;
private boolean confirmed;
}
Try using #JoinColumn instead of #PrimaryKeyJoinColumn.
#OneToOne
#JoinColumn(name="OWNER", referencedColumnName="USERID")
private User ownerUserBean;
#OneToOne
#JoinColumn(name="GUARDIAN", referencedColumnName="USERID")
private User guardianUserBean;
According to spec the latter should be used to join the primary table of an entity subclass in the JOINED mapping strategy to the primary table of its superclass (exact definition available here)
Related
I want to get an object of EventDate using primary key. Following is the query i'm executing
EventData eventData = entityManager.find(EventData.class, eventdataid);
After executing this command in console i'm getting the query as
select eventsajgj0_.FILE_ID as FILE_ID8_14_0_, eventsajgj0_.id as
id1_12_0_, eventsajgj0_.id as id1_12_1_, eventsajgj0_.CODE as CODE2_12_1_,
eventsajgj0_.DATE as DATE3_12_1_, eventsajgj0_.FILE_ID as FILE_ID8_12_1_,
eventsajgj0_.MILLIS as MILLIS4_12_1_, eventsajgj0_.ORDER_NR as
ORDER_NR5_12_1_, eventsajgj0_.TYPE as TYPE6_12_1_, eventsajgj0_.VALUE as
VALUE7_12_1_ from eventdata eventsajgj0_ **where eventsajgj0_.FILE_ID=?**
order by eventsajgj0_.ORDER_NR
Please note the where clause in above query is against file_id(foreign key) and not id(eventdata primary key)
The dao structure is as follows
public class EventData implements Serializable {
private static final long serialVersionUID = 1L;
public EventData() {
}
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="FILE_ID")
private ApplicationFile file;
getter & setters
}
public class ApplicationFile implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
bi-directional many-to-one association to Event
#OneToMany(mappedBy="file", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#OrderBy("orderNr")
private List<EventData> eventsajgjd;
getter & setters
}
my question is, why is it querying using file_id and not id when i'm executing a query on eventdata table.
PS:if i change fetch type of ApplicationFile as LAZY then the query executed is on id and not on file_id.
(added from Comment:)
CREATE TABLE eventdata (
ID int(11) NOT NULL AUTO_INCREMENT,
FILE_ID int(11) DEFAULT NULL,
PRIMARY KEY (ID),
KEY eventdata_ibfk_1 (FILE_ID),
CONSTRAINT eventdata_ibfk_1 FOREIGN KEY (FILE_ID)
REFERENCES files (ID) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=297502 DEFAULT CHARSET=utf8
I bet because you mapped the EventData / ApplicationFile bidirectionally (you have an attribute of type List<EventData> in ApplicationFile entity
So loading an EventData means eagerly loading the related ApplicationFile and so eagerly loading all related EventData.
I suppose that the related ApplicationFile instance is already in EntityManager L1 cache (otherwise the query should join on files table)
I have a class User with primary key (id) which corresponds to 'user' table in SQL Server database. My User class has a many to one relationship with Project Entity.
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#JoinColumn(name = "project", referencedColumnName = "id_project")
#ManyToOne(optional = false)
private Project project;
}
database:
CREATE TABLE [dbo].[user](
[id] [int] IDENTITY(27,1) NOT NULL,
[name] [varchar](45) NULL,
[project] [int] NOT NULL,
CONSTRAINT [PK_user_1] PRIMARY KEY CLUSTERED
(
[id] ASC)
Now in my database I need to have two rows with same user ids but different projects so I changed my primary key to a composite primary key in my user table.
user table: id name id_project
---------------------------
1 John 5
1 John 6
project table: id name
---------------
5 Project5
6 Project6
and changed my table like this
CREATE TABLE [dbo].[user](
[id] [int] IDENTITY(27,1) NOT NULL,
[name] [varchar](45) NULL,
[project] [int] NOT NULL,
CONSTRAINT [PK_user_1] PRIMARY KEY CLUSTERED
(
[id] ASC,
[project_mode] ASC)
My question is that: Was this step required in my table? and if it was, then how can I change the #id of my User class?
JPA 2.1 allows derived IDs, allowing you to mark relationships as being part of the ID. User would look like this:
#Entity
#IdClass(UserPK.class)
public class User{
#Id
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Id
#JoinColumn(name = "project", referencedColumnName = "id_project")
#ManyToOne(optional = false)
private Project project;
}
public class UserPK{
private Integer id;
private Integer project;//use the same type as the Project's ID.
}
I'd seriously recommend to have a look at the normal forms of relational databases. Especially the Third Normal Form.
In this case I'd keep the table user without the column id_project. Instead you can create a third table user_project with the column id_user and id_project, both part of the primary key. This is the usual way to model an n:m relationship in a relational database.
I'm a little new to JPA and I'm trying to get my entity classes to work properly, but my code is always returning me this error: "Multiple writable mappings exist for the field [PRODUTOS.MARCA_IDMARCA]"
These are my entity classes:
#Entity
public class Marca implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer idMarca;
private String nome;
private Integer idFornecedores;
.
.
.
}
I'm trying to make an unidirectional ManyToOne relationship between produtos and Marca (1 Marca to Many produtos):
#Entity
public class produtos implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long idProdutos;
private int codBarras;
private String nome;
private float preco;
private int qtdProduto;
private int idCategoriaProduto;
#ManyToOne
private CategoriaProduto categoria;
private int Marca_idMarca;
#ManyToOne
private Marca marca;
.
.
.
}
Error:
Exception Description: Multiple writable mappings exist for the field [PRODUTOS.MARCA_IDMARCA]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.ManyToOneMapping[marca]
Descriptor: RelationalDescriptor(ClassesDeEntidade.produtos --> [DatabaseTable(PRODUTOS)])
I really don't know what's happening. Can anyone explain to me why this code is wrong?
EDIT:
Table definitions:
Produtos:
CREATE TABLE IF NOT EXISTS mydb.Produtos (
idProdutos INT NOT NULL AUTO_INCREMENT,
codBarras INT(13) NOT NULL,
nome VARCHAR(150) NOT NULL,
preco FLOAT NOT NULL,
qtdProduto INT NOT NULL,
idCategoriaProduto INT NOT NULL,
Marca_idMarca INT NOT NULL,
PRIMARY KEY (idProdutos),
INDEX fk_Produtos_CategoriaProduto1_idx (idCategoriaProduto ASC),
INDEX fk_Produtos_Marca1_idx (Marca_idMarca ASC),
CONSTRAINT fk_Produtos_CategoriaProduto1
FOREIGN KEY (idCategoriaProduto)
REFERENCES mydb.CategoriaProduto (idCategoriaProduto)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT fk_Produtos_Marca1
FOREIGN KEY (Marca_idMarca)
REFERENCES mydb.Marca (idMarca)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Marca:
CREATE TABLE IF NOT EXISTS mydb.Marca (
idMarca INT NOT NULL AUTO_INCREMENT,
nome VARCHAR(45) NOT NULL,
idFornecedores INT NOT NULL,
PRIMARY KEY (idMarca),
INDEX fk_Marca_Fornecedores1_idx (idFornecedores ASC),
CONSTRAINT fk_Marca_Fornecedores1
FOREIGN KEY (idFornecedores)
REFERENCES mydb.Fornecedores (idFornecedores)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Can you show your table definitions?
The problem seems to be that you have two fields (probably Marca marca, which have the #ManyToOne annotation and int Marca_idMarca, that has the same name) mapping to the same PRODUTO.ID_MARCA column in the database.
I know I should post this as a comment, but I'm not able to comment yet.
I have a simple model in Play Framework 2, and I would like to specify a default value to be inserted on a specify INT column if none is provided when the INSERT is performed.
Model:
#Entity
#Table(name = "DashboardOptions", schema = "dbo")
public class DashboardOptions extends Model implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
public Long id;
#Basic(optional = false)
#Column(name = "userId")
public Long userId;
#Column(name = "chartType")
public String chartType;
public String name;
public Integer size = 2;
I'd like to have the size column populate with 2 by default, however, if I specify the default value as above, my database evolution does not reflect this:
create table dbo.DashboardOptions (
id numeric(19) identity(1,1) not null,
userId numeric(19) not null,
chartType varchar(255),
name varchar(255),
size integer,
constraint pk_DashboardOptions primary key (id))
;
What I would expect to see is this:
create table dbo.DashboardOptions (
id numeric(19) identity(1,1) not null,
userId numeric(19) not null,
chartType varchar(255),
name varchar(255),
size integer default 2,
constraint pk_DashboardOptions primary key (id))
;
Use own columnDefinition like this:
#Column(columnDefinition = "integer default 2")
public Integer size = 2;
Another option is to use #PrePersist tag package javax.persistence. you can have a method decorated in your bean with #PrePersist and that method is called before Ebean.save call. so in this case the following code would set the default value of size to 2.
#PrePersist
protected void onCreate {
if (this.size == null)
this.size = 2;
}
This approach is applicable only within the context of ORM (Ebean) and obviously wouldn't work directly with SQL. The advantage of this method is that this is more database neutral in the sense that integer default 2 might not be a valid column definition string in some unknown strange RDBMS systems.
I have this scenario :
tbl_master(
master_field_pk int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`master_field_pk`)
)
and
tbl_detail(
`detail_field_pk` int(11) NOT NULL AUTO_INCREMENT,
`master_field_pk` int(11) DEFAULT NULL,
CONSTRAINT `child_fk1` FOREIGN KEY (`master_field_pk`) REFERENCES `tbl_master` (`master_field_pk`) ON DELETE NO ACTION ON UPDATE NO ACTION,
PRIMARY KEY (`master_field_pk`)
)
This is my hibernate class for master :
#Entity
#Table(name = "tbl_master")
#Name("TblMaster")
public class TblMaster implements Serializable{
#Id
#Column(name = "master_field_pk")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer MasterFieldPk;
#Cascade({CascadeType.ALL,CascadeType.DELETE_ORPHAN})
#OneToMany(mappedBy="tblMaster")
#JoinColumn(name="master_field_pk", insertable=true, updatable=true, referencedColumnName="master_field_pk")
private Set<TblDetail> tblDetails;
#Transient
private List<TblDetail> tblDetailsList;
// any other things
}
and this one is for detail :
#Entity
#Table(name="tbl_detail")
#Name("TblDetail")
public class TblDetail implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="detail_field_pk")
private Integer detailFieldPk;
#Column(name="master_field_pk")
private Integer MasterFieldPk;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="master_field_pk", insertable = false, updatable = false, referencedColumnName = "master_field_pk")
private TblMaster tblMaster;
// any other things
}
To insert, i use this block of code (more or less) :
TblDetail detail_1 = ...
detail_1.setTblMaster(tblMaster);
// and so on ..
Set<TblDetails> set = ...
set.add(detail_1);
// and so on...
tblMaster.setTblDetails(set);
em.persist(tblMaster);
em.flush();
All the records (the master record and the details record) are perfectly inserted. However, the master_field_pk in tbl_detail - the foreign key - is still null. Is it possible to avoid this behavior? Thanks
No. You have to persist the master first. The detail rows need to know what the master ID is to be linked.
This can be done but only when the key is assigned by the application, for example GUID keys assigned by the application, or some sort of key server.
CascadeType.ALL should do the trick for you. It has worked for me all the time.
I think your problem lies in your #GeneratedValue attribute strategy. You should be using GenerationType.IDENTITY instead of GenerationType.AUTO.