I'd like JPA EclipseLink creates tables exactly as is the ClassName (and fields) WITHOUT annotation #Table and #Column. It's always creates tables and fields with UPPERCASE, which makes readability difficult in the DB console.
ex.:
#Entity
public class ChannelEntity {
#Id #GeneratedValue
public Long id;
public String name;
public String description;
public Boolean oficial;
#Temporal(TemporalType.TIMESTAMP)
public Date creation;
}
And I'd like results in
Table: ChannelEntity
id creation description name oficial
----------------------------------------------------
351 NULL meu desc meu nome 1
Maybe exist same parameter in persistence.xml, but I can't find it.
If EclipseLink behaves correctly, the parameter you are looking for is delimited-identifers. From the JPA 2.1 spec:
It is possible to specify that all database identifiers in use for a
persistence unit be treated as delimited identifiers by specifying the
<delimited-identifiers/> element within the persistence-unit-defaults
element of the object/relational xml mapping file. If the
<delimited-identifiers/> element is specified, it cannot be
overridden.
If this element is included, EclipseLink should delimit all database identifiers in its generated SQL, which would cause the database objects to be created with case-sensitive names.
Related
I'm implementing a custom hibernate JPA naming strategy in a Hibernate 5.4.32 model.
The implicit naming strategy is setup to convert camelCase property names to snake_case. Thus a property called myKey will turn into a column called my_key. This part works correct, and all columns have the correct names and all JPA interactions work.
However I have an issue with generating index names. In that Strategy interface there is a method:
public Identifier determineIndexName(ImplicitIndexNameSource source)
The parameter "source" passed in that method is ambiguous in how it handles the column names that are passed to the method.
For instance if I create the following entity
#Table(schema = LoaderModel.SCHEMA, name = "main_entity",indexes = {#Index(columnList = "other_id,myKey")} )
#Entity
public class MainEntity{
#ManyToOne
OtherEntity other;
#Column(columnDefinition = "text")
String myKey;
}
This entity creates a table with two columns, other_id, and my_key. But notice how in the #Index I must use the property name "myKey", even though for the ManyToOne column I have to use the actual column name: "other_id". This is in line with what I see in the determineIndexName method:
#Override
public Identifier determineIndexName(ImplicitIndexNameSource source) {
String name = "ix_"+source.getTableName().getText()+"_"+String.join("_",source.getColumnNames().stream().map(Identifier::getText).collect(Collectors.toList()));
//The name here is: ix_main_entity_other_id_myKey
//instead of the desired: ix_main_entity_other_id_my_key
return Identifier.toIdentifier(name);
}
I can't find any properties of the ImplicitIndexNameSource object that allow me to detect the actual column names of the properties in the index. In addition I'm not sure why it passes the column name for entity references, but the property name for entity properties.
I'm guessing some portion of this weird ambiguity is intended, but If I could simply get access to the real column name then I could write the code to handle it.
Note: Manually configuring the column name "my_key" for the myKey property seems to fix this issue, but that defeats the purpose of creating the naming strategy (which would be applied to the entire model), so that's not a valid fix.
I am trying to do something similar like below.
#Entity
#Table(name="Sample")
public record Sample(Integer id, String name) {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="user_id")
private Integer id;
#Column(name="username")
private String name;
}
However, it gives me error "User declared non-static fields id are not permitted in a record"
and same for name field as well.
Is there a way to use new java feature "record" with JPA annotation?
See the article, Using Records as Projections in JPA by Billy Korando. The following is a brief summary.
Records cannot be Entities
Jakarta Persistence (JPA; formerly Java Persistence API) implementations such as Hibernate depend on features either forbidden or not recommended by the JEP 395: Records spec: no-arg constructors, non-final fields, setters, etc.
➥ So, no, records cannot be used as JPA Entity.
Other uses of records
You can use records with:
CriteriaBuilder
TypedQuery
NativeQuery
Mapping definition
Spring data has some support as well.
See that article linked above for details, and for links to two other articles.
I'm developing a code generator that have to generate JPA entities from database meta-model files. These model are from home-brewed modeling system which are being used to generate models other than JPA entities.
In these models some fields are mapping back to same database column. But it seems like JPA does not like that very much. When I try to run generated code I get
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [FACT_INVENT_TRANS_HIST_DM.TRANSACTION_ID]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[TransactionIdKey-->FACT_INVENT_TRANS_HIST_DM.TRANSACTION_ID]
Descriptor: RelationalDescriptor(InventTransHistFactDM --> [DatabaseTable(FACT_INVENT_TRANS_HIST_DM)])
As I can't change the models only option left is to make one of those fields read-only. And the JPA entities being generated are only used to read data from database it will not used for writing data. Is there a way to mark some fields as read only or tell EclipseLink that these entities are read only so it does not have to worry about the multiple writable mapping.
I tried using EclipseLink's #ReadOnly annotation in all entities but it did not help this issue.
There is no #ReadOnly in JPA.
There are however attributes "insertable"/"updatable" that you can set against a field via #Column to effectively do the same.
The question may be almost 6 years old, but it's still being found today, so I'd like to address another option:
public class Foobar {
#OneToOne
#JoinColumn(name="SELF_COLUMN_FOO", referencedColumnName = "FOREIGN_COLUMN_TO_JOIN")
public Foo foo;
#OneToOne
#JoinColumn(name="SELF_COLUMN_BAR", referencedColumnName = "FOREIGN_COLUMN_TO_JOIN")
public Bar bar;
}
This can be used where SELF_COLUMN is obviously the relevant column in the Foobar table, and FOREIGN_COLUMN_TO_JOIN would be single key in the other table you wish to join.
This will be useful where you want to have two (or more) attributes in a single class, but only one column to join on the foreign DB table. For example: An Employee may have a home phone number, cell number, and a work phone number. All are mapped to different attributes in the class, but on the database there's a single table of phone numbers and id's, and an identifier column, say VARCHAR(1) with 'H' or 'W' or 'C'. The real example would then be...
Tables:
PHONENUMBERS
PHONENUMBER_ID,
ACTUAL_NUMBER
EMPLOYEE
ID
HOMENUMBER VARCHAR(12),
CELLNUMBER VARCHAR(12),
WORKNUMBER VARCHAR(12)
public class Employee {
#OneToOne
#JoinColumn(name="HOMENUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone homeNum;
#OneToOne
#JoinColumn(name="CELLNUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone cellNum;
#OneToOne
#JoinColumn(name="WORKNUMBER", referencedColumnName = "PHONENUMBER_ID")
public Phone workNum;
}
As you can see, this would require multiple columns on the Entity's table, but allows you to reference a foreign key multiple times without throwing the 'Multiple writable mappings exist...' that you showed above. Not a perfect solve, but helpful for those encountering the same problem.
When defining a JPA Entity like this:
#Entity
#Table
public class CaseExample implements Serializable {
#Id
Long id;
#Basic
String fooBar;
}
the automatically created SQL table name is "CASEEXAMPLE" and the column name "FOOBAR".
How can I change that from upper-case to lower-case-with-underscore e.g. "case_example" and "foo_bar" without having to add a name="foo_bar" to every single #Table and #Column?
Is the naming strategy defined by JPA or implemenation dependend? I use JPA 2.0 with EclipseLink 2.5.0.
JPA standardizes the names. I would leave them using the standard, or use #Column to change specific ones.
With EclipseLink you could modify you column names using your own code in a DescriptorCustomizer or SessionCustomizer. You would just iterate over your descriptor's mapping and reset the fieldNames based on your naming convention.
According to my JPA 2.0 book (and online documentation), I should be able to mix field and property access within a single entity or entity hierarchy. The annotation of #Access on the class specifies the default access. When placed on a field or property getter #Access can specify that the default should be overridden for this field.
#Entity
#Access(AccessType.FIELD)
Class Foo {
#Id
int id;
#Column(name = "myfield")
String myField;
#Column(name = "myProp")
#Access(AccessType.PROPERTY)
public int getMyProp () {
return 3;
}
public void setMyProp (int p) {
// do nothing
}
}
This class should result in a table with three columns. However it doesn't with Hibernate...the "myProp" column is missing from the table because apparently Hibernate takes its field vs property cue from the entity ID and runs with it...totally ignoring the JPA spec with regards to #Access.
Can anyone confirm this or did I make a stupid mistake somewhere?
I've seen similar (not the same but similar) issues like HHH-5004 so I wouldn't exclude that this might be a new one (the TCK doesn't seem exhaustive). But what version of Hibernate are you using? Did you try with the latest?
Based on the docs your code seems to be right. The #Access(AccessType.FIELD) annotation on top is unnecessary, because you annotated the field int id;
This tells hibernate to use field access. I tried a very similar example with annotations and xml config mixed. This leads to the same behaviour, so it's probably a bug in hibernate.
I tried with hibernate 3.5.3