I'm building an application based on JavaEE7 with JPA 2.1 and EJB 3.2.
I'm trying to define entity with JPA based on the table from an Oracle database. I use EclipseLink as implementation of JPA.
My case is to retrieve translation stored on the database. All the translation are stored on a single table and identified with a key and a language id.
I can't change the way the database is organised and I'm a beginner in JPA.
Here an example of lines from the translation table:
TRANSLATION:
TRANS_ID | TRANS_KEY | TRANS_VALUE | LAN_ID
1 | APPVERSION_APPV_COMMENT_1215 | Du texte | 1
2 | APPVERSION_APPV_COMMENT_1215 | Some text | 2
Where LAN_ID : 1 correspond to the french translation and LAN_ID : 2 is the english translation.
The entity of TRANSLATION looks like this:
#Entity
#Table(name = "TRANSLATION")
#NamedQueries({
#NamedQuery(name="Translation.findAll", query="SELECT trans FROM Translation trans")
})
public class Translation implements Serializable {
#Id
#Column(name = "TRANS_ID", nullable = false)
private Integer translationId;
#Column(name = "TRANS_KEY", nullable = false)
private String translationKey;
#Column(name = "TRANS_VALUE")
private String translationValue;
#Column(name = "LAN_ID", nullable = false)
private Integer languageId;
}
And here is a possible case for a table using translations:
APPVERSION :
APPV_ID | APPV_COMMENT
0 | APPVERSION_APPV_COMMENT_1215
1 | (null)
2 | some texte with no reference to translation table
Here are the tree possible case I can have in column witch use translation. The third case is due to historical reasons.
I would like to implement a JPA entity to represent the table APPVERSION where I could retrieve the translation without using JPQL.
I tried an entity looking like this (simplified version):
#Entity
#Table(name = "APPVERSION")
#NamedQueries({
#NamedQuery(name="AppVersion.findAll", query="SELECT app FROM AppVersion app")
})
public class AppVersion implements Serializable {
#Id
#Column(name = "APPV_ID", nullable = false)
private Integer applicationVersionId;
#OneToMany()
#JoinColumn(name = "TRANS_KEY", referencedColumnName = "APPV_COMMENT", nullable = true)
//As a beginer who don't get it all I also tryed the following. With quite the same result
//#JoinColumn(name = "APPV_COMMENT", referencedColumnName = "TRANS_KEY", nullable = true)
private List<Translation> applicationVersionComment;
}
With, the following
#JoinColumn(name = "TRANS_KEY", referencedColumnName = "APPV_COMMENT", nullable = true)
I got an error saying:
Caused By: Exception [EclipseLink-6094] (Eclipse Persistence Services - 2.6.1.v20150916-55dc7c3): org.eclipse.persistence.exceptions.QueryException
Exception Description: The parameter name [APPV_COMMENT] in the query's selection criteria does not match any parameter name defined in the query.
And with the following
#JoinColumn(name = "APPV_COMMENT", referencedColumnName = "TRANS_KEY", nullable = true)
I got an error saying:
Caused By: Exception [EclipseLink-6094] (Eclipse Persistence Services - 2.6.1.v20150916-55dc7c3): org.eclipse.persistence.exceptions.QueryException
Exception Description: The parameter name [TRANS_KEY] in the query's selection criteria does not match any parameter name defined in the query.
I would like to know how to handle such case with JPA entity. For historical reason the data are quite messy and sadly I have to deal with it as it is.
Related
I was hopping to find an answer to my probleme on this here forum. My problem is as follows, I have two classes :
#Entity
#Table(name = "a")
public class A implements Serializable{
#Id
private String id = UUID.randomUUID().toString();
#Column(name = "REFERENCE_ID")
private String referenceId;
#Column(name = "VERSION")
private String version;
}
And
#Entity
#Table(name = "b")
public class B{
#Id
private String id = UUID.randomUUID().toString();
#Column(name = "REFERENCE")
private String reference;
#ManyToMany(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value =
"(select r from A r where r.reference_id = reference_id order by r.version desc limit 1)",
referencedColumnName = "reference_id")),
#JoinColumnOrFormula(column = #JoinColumn(name = "reference_id",
referencedColumnName = "reference_id", insertable = false))
})
private A referenceId;
}
The thing is reference_id is not a unique key in the b table and was just an indicative value in table A so in order to fetch the entire correspondent row I had to do some filtering with the formula in my join annotation.
When I try to fetch my data I get the following error
[Request processing failed; nested exception is
org.springframework.dao.InvalidDataAccessResourceUsageException:
could not extract ResultSet; SQL [n/a]; nested exception is
org.hibernate.exception.SQLGrammarException: could not extract
ResultSet] with root cause org.postgresql.util.PSQLException:
ERROR: relation "a" does not exist Position : 309
EDIT
ACtually t works as intended when changing my join formula to
#JoinFormula(value =
"(select r from schema_A r where r.reference_id = reference_id order by r.version desc limit 1)",
referencedColumnName = "reference_id"))
the problem now is that the code is intended to work on multipple envirnments
as for my application.yml it looks a bit like this;
jpa:
database: POSTGRESQL
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
default_schema: schema
jdbc:
lob:
non_contextual_creation: true
time_zone: UTC
Thanks for your responses :)
I agree with Simon's comment. For Postgres (and relational databases in general), the word "table" and "relation" are the same and where the term "relational" comes from. So, when it says "Can't find relation B" it literally means "Can't find a table called B".
You should check your connection settings for the schema to see if those tables have/haven't been defined. If it's not obvious, maybe add/edit the question accordingly with your connection settings & appropriate debugging showing you DO see the relations (tables) there.
I would like to know why spring jpa isn't deleting the rows that are causing a constraint error or a way to make it possible to edit my Account entity with new roles.
The following entities are involved in the action i'm trying to perform.
#Entity
#Table(name = "account")
class AccountEntity(uuid: UUID? = null,
#Column(nullable = false) val email: String,
#Column(nullable = false) val password: String,
#OneToMany(
mappedBy = "accountUuid",
cascade = [CascadeType.ALL],
fetch = FetchType.LAZY
) val accountRolesEntity: List<AccountRolesEntity>) : BaseEntity(uuid)
#Entity
#Table(name = "account_roles")
class AccountRolesEntity(uuid: UUID? = null,
#Column(nullable = false) val accountUuid: UUID,
#OneToOne val role: RoleEntity) : BaseEntity(uuid)
#Entity
#Table(name = "role")
class RoleEntity(uuid: UUID? = null,
#Column(nullable = false) val name: String ) : BaseEntity(uuid)
So i'm trying to update the roles of a specific account.
For example:
If X has roles 'viewer' and 'editor' and suppose i want to change it to viewer only.
I do the following steps:
Request account entity from database
set new accountRolesEntity (received from controller) to account
Call the jpa repository save method
Method in Service class:
fun updateExistingAccount(account: AccountDTO, adjustedRoles: List<RoleDTO>): AccountDTO {
val mappedRoles: List<AccountRolesEntity> = adjustedRoles.map { accountRolesMapper.map(account.uuid, it) }
val accountEntity = accountMapper.map(account, mappedRoles)
return accountMapper.map(accountRepository.save(accountEntity))
}
The error i'm getting is: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "account_roles_account_uuid_role_uuid_key"
This is because i have a constrain in my database to make sure that an account may not have duplicate roles. The create table statement is as following:
CREATE TABLE account_roles (
uuid UUID PRIMARY KEY,
account_uuid UUID NOT NULL REFERENCES account(uuid),
role_uuid UUID NOT NULL REFERENCES role(uuid),
UNIQUE (account_uuid, role_uuid)
);
There is a fix for this by performing all the actions 1 by 1: Delete first and then make new inserts. But there should be a better way for this.
actually "AccountRolesEntity" is not an entity, it's a table which keeps the relationships of two entity by keeping ids of that entities into the table.
so for the first step, you should have something like this,
#JoinTable(name = "account_role",
joinColumns = #JoinColumn(name = "account")
, inverseJoinColumns = #JoinColumn(name = "role"))
#OneToMany(mappedBy = "accountUuid",
cascade = [CascadeType.ALL],
fetch = FetchType.LAZY)
val accountRolesEntity: List<AccountRolesEntity>) :BaseEntity(uuid)
and I think your problem depends on your cascades and your class diagram so check them again.
I have an entity that is defined with JPA annotations (only a few fields of interest shown here)
#Entity
public class Rule implements Serializable, Cloneable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO,
generator = "SEQ_STORE")
#Column(name = "RULE_ID",
nullable = false)
private final Long id = null;
#Column(name = "CODE",
length = 25,
nullable = false)
private String code;
#Column(name = "DESCRIPTION",
length = 250,
nullable = true)
private String description;
#Column(name = "VALIDATION_FIELDS",
length = 250,
nullable = true)
private String validationFields;
#ExportField("EXPRESSION")
#Lob
#Column(name = "EXPRESSION",
nullable = true)
private String expression;
#Lob
#Column(name = "ACTION",
nullable = true)
private String action;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "PARENT_ID",
nullable = true,
foreignKey = #ForeignKey(name = "FK_XTB_RULE_2_PARENT") )
private Rule parent;
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "RULESET_ID",
nullable = false,
foreignKey = #ForeignKey(name = "FK_XTB_RULE_2_RULESET") )
private RuleSet ruleSet;
}
#Entity
public class RuleSet implements Serializable, Cloneable
{
private static final long serialVersionUID = 7982682149517239983L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO,
generator = "SEQ_STORE")
#ExportField("RULESET_ID")
#Column(name = "RULESET_ID",
nullable = false)
private final Long id = null;
#JsonIgnore
#OneToMany(mappedBy = "ruleSet",
fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
#OrderBy("position")
private List<Rule> rules = new LinkedList<Rule>();
}
Then I have a method that generates a tree of these Rules (see the backreference) and puts all the rules into the List contained in the Ruleset entity.
The auto-generated DDL makes columns suitable for large expressions as the column is #Lob annotated
On Mysql and Oracle I can successfully run the code that populates the rules table (I run Bamboo tests that create DB from scratch every time). Hoever, when the testing is run oh HSQLDB, Hibernate's insert of the Ruleset object fails
2016-04-29 13:09:26,946 WARN [localhost-startStop-1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - logExceptions - SQL Error: 3401, SQLState: 22001
2016-04-29 13:09:26,949 ERROR [localhost-startStop-1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - logExceptions - data exception: string data, right truncation; table: XTB_RULES column: EXPRESSION
2016-04-29 13:09:39,965 ERROR [localhost-startStop-1] it.phoenix.web.data.managers.spring.ModuleManagerImpl - init - could not execute statement
org.hibernate.exception.DataException: could not execute statement
...
Caused by: org.hsqldb.HsqlException: data exception: string data, right truncation; table: XTB_RULES column: EXPRESSION
...
Caused by: org.hsqldb.HsqlException: data exception: string data, right truncation
There is an expression long 353 character in my code, I have almost found the "guilty" object.
But the problem is that even with HSQLDB the following DDL is generated
create table XTB_RULE_FUNCTIONS (RULE_FUNCTION_ID bigint identity not null,
DESCRIPTION varchar(250),
ENABLED bit not null,
EXPRESSION varchar(MAX) not null,
NAME varchar(50) not null,
OBJECT_TYPE varchar(20) not null,
POSITION bigint not null,
primary key (RULE_FUNCTION_ID));
EXPRESSION is supposed to be VARCHAR(max), so it should accommodate any string.
But my insertion still fails. I have no mean to check the actual in-memory database at the moment
Other info on the application:
Is a web application running on Tomcat
I use Bamboo CI to run tests on different databases, each loading the Spring context instead of being run in a servlet container
Part of Spring initialization is to populate, along others, the Ruleset table if data does not exist. Since Bamboo recreates DB at every run (especially in-memory one), I always need to populate rules declared programmatically somewhere in a piece of code useless to paste here
I have read this but I have checked that DDL script declares VARCHAR(max) so I don't think that applies to me
Again and again, the same code works on other DBs
Still, I need to store data larger than 255 characters
Any idea on how to fix? In my unit testing I may still comment out one of the entities, but that is only a workaround.
I am trying to fetch the list of records from a view which has a composite primary key with three columns.
I tried to embed the composite key in the entity class. But I am getting the below mentioned errors. The columns of the views (VW_ALERTS) are C_ID, MAT_ID, P_MONTH, CO_TYPE, CO_SUBTYPE.
Here the composite keys are C_ID, MAT_ID, P_MONTH. I am making the property of them in the embeddable class.
Please help to resolve the issue
org.hibernate.QueryException: could not resolve property: coreId of: com.sp.cpem.dto.VwAlerts [FROM com.ct.cpem.dto.VwAlerts d ORDER BY d.cId ASC]
This following code is used to execute the hql.
Session session = sessionFactory.openSession();
String hql = "FROM VwAlerts d ORDER BY d.coId ASC";
Query query = session.createQuery(hql);
return query.list();
The entity class :
#SuppressWarnings("unchecked")
#Entity
#Table(schema = "TIGER", name = "VW_ALERTS")
public class VwAlerts {
#Embedded
private VwAlertsPK vwAlertsPK;
#Basic
#Column(name = "CO_TYPE", nullable = true)
private String coType;
#Basic
#Column(name = "CO_SUBTYPE", nullable = true)
private String coSubType;
Class used to get the composite key
#Embeddable
public class VwAlertsPK implements Serializable {
#Basic
#Column(name = "C_ID", nullable = false)
private BigDecimal cId;
#Basic
#Column(name = "MAT_ID", nullable = true)
private BigDecimal matId;
#Basic
#Column(name = "P_MONTH", nullable = true)
private BigDecimal pMonth;
I am expecting to get all the records from the view.
I tried with the #Id column in the entity class, it failed by returning only the duplicate records of the first row from the view.
Your entity VwAlerts has only 3 properties --> vwAlertsPK, coType, coSubType
but in your HQL you are trying to access a property coreId which does not exist in your entity.
FROM com.ct.cpem.dto.VwAlerts d ORDER BY d.coreId ASC
So add the property coreId to your entity or else just update the ORDER BY clause so you are pointing to correct properties of your entity.
I have two java-classes / db-tables: 'message' and 'thirdparty'
#Entity
public class Message {
#OneToOne(mappedBy = "message")
private ThirdParty source = null;
#OneToOne(mappedBy = "message")
private ThirdParty target = null;
....
}
#Entity
public class ThirdParty {
#OneToOne(targetEntity = Message.class)
#JoinColumn(name = "Message", referencedColumnName = "mess_id", nullable = false)
private Message message = null;
#Column(name = "isSource", nullable = false)
private Boolean isSource = null;
}
Message has two references to ThirdParty, which could be differenced by isSource (if they are source or target).
This cannot be resolved by jpa they way it is designed / annotiated. But is there a way to to this by adding some annotiation or some kind of special sql-statement?
This is conceptually wrong. You cannot do this. OneToOne mapping occurs when there are two entities mapped in the following way :
Entity1 : Has a primary key(PK1) and others along with a foreign key(FK)
Entity2 : Has a primary key (PK2).
Now the FK is mapped to PK2 in such a way that for each occurence of PK there must be a one and only one matching occurence of FK.