Problem using #SecondaryTable in Hibernate - java

Abridged version of my schema:
utility_company
id int not null -- PK
name varchar(255) not null
utility_settings
utility_id -- FK to utility
use_magic tinyint(1) not null default 0
There is a one-to-one mapping between these two tables. Setting aside the fitness of this design, I want to Map the data in both of these tables to one object. In Hibernate/JPA, this is allegedly done as follows:
#Entity
#Table(name = "utility_company")
#SecondaryTables({
#SecondaryTable(
name = "utility_settings",
pkJoinColumns = {
#PrimaryKeyJoinColumn(
name="utility_id", referencedColumnName="id")
})
})
public class UtilityCompany extends AbstractEntity {
And so forth.
Every #Column includes the appropriate table name.
When I deploy, I get this error:
Cannot find the expected secondary table:
no utility_company available for poscore.model.UtilityCompany
The utility_company table is definitely there (a previous version only maps UtilityCompany to the utility_company table; I'm adding the utility_settings).
Found numerous forum posts with this exact problems and no answers. I've also tried various allegedly legal forms of specifying the #SecondaryTable all of which have the same effect.
Anyone successfully use #SecondaryTable, and, if so, seen this?

"Every #Column includes the appropriate table name."
Try removing the explicit table name for the first table name columns, only specifying it for the secondary table columns. Did the trick for me.

Your mappings are correct IMHO, and runs fine with DataNucleus AccessPlatform as the JPA implementation. Maybe Hibernates log tells you more ?
--Andy DataNucleus

Related

Weblogic to Liberty w JPA upgrade - related entities intermittently not being queried

just a quick question please in case something stands out immediately.
We're migrating an EAR/EJB application from Weblogic 11g to latest WS Liberty (22.x) also upgrading several of the frameworks including JPA to 2.2. This also changes JPA implementation to eclipseLink. We came from com.oracle.weblogic.11g.modules:javax.persistence:1.0.0.0_1-0-2. Underlying DB is MS-SQL Server.
And I'm running into some weirdness with regards to related objects not being resolved/queried intermittently.
Just as an example we have entities where the columns hold reference data codes or similar lookups. Say I have an entity called PayemntRecordT and it has a status code which refers to a ref table that also holds a textual description. Something like this:
SQL:
CREATE TABLE [PAYMENT_RECORD_T](
[PAYMENT_ID] [int] NOT NULL,
...
[PAYMENT_STATUS_CD] [CHAR](8) NOT NULL,
...
)
ALTER TABLE [PAYMENT_RECORD_T] WITH CHECK ADD CONSTRAINT [FK_PAYM4] FOREIGN KEY([PAYMENT_STATUS_CD])
REFERENCES [RECORD_STATUS_T] ([REC_STAT_CD])
GO
CREATE TABLE [RECORD_STATUS_T] (
[RECORD_STAT_CD] [CHAR](8) NOT NULL,
[RECORD_STAT_DSC] [VARCHAR](60) NOT NULL
CONSTRAINT [PK_RECORD_STATUS_T] PRIMARY KEY CLUSTERED (
[RECORD_STAT_CD] ASC
)WITH (PAD_INDEX = OFF...) ON [PRIMARY]
) ON [PRIMARY]
GO
Java:
#Table(name = "PAYMENT_RECORD_T")
#Entity
public class PaymentRecordT {
...
#ManyToOne
#PrimaryKeyJoinColumn(name = "payment_status_cd", referencedColumnName = "REC_STAT_CD")
private RecordStatusT recordStatusT;
}
#Table(name = "RECORD_STATUS_T")
#Entity
public class RecordStatusT {
#Column(name = "REC_STAT_CD")
#Id
private String recStatCd;
#Column(name = "REC_STAT_DSC")
#Basic
private String recStatDsc;
}
Others relations in our app might not be primary key relations but loose relations in which case its just #JoinColumn but the pattern would be the same.
My 'weirdness' is the following:
So in this example I have a list of 10 'Payment Records' each of them have such a record status, which is actually NON NULL in the database. When I do the initial retrieval via EJB method it grabs the 10 records and I also get the correctly resolved/queried record statuses.
Then I add a new record via EJB method (TRANSACTION_REQUIERD). After the add method returns I can query the new payment record in the database via SSMS. Its committed and it looks 100% correct and it contains a correct record status code.
Now I run the retrieval method again and I get the 11 records as I would expect. Only the 11th (newly inserted) record will have recordStatusT as null.
When I restart the app all goes well again for the retrieval of all 11 records. But for subsequent additions the outcome seems again 'undefined'.
In JDBC logging I an see that during the original retrieval of the records the record_status_t table was queried but the 2nd time around it was not and I have no explanation why.
I played with FETCHTYPE.EAGER and read up on caching etc but I'm not going anywhere.
Any ideas?
Thanks for your time
Carsten
I solved the problem by ensuring that after inserts/updates the objects arent being queried from the cache.
In the end - rather than doing it with query hint - I disabled caching for the entity involved using the #Chacheable annotation, like so
#Table(name = "PAYMENT_RECORD_T")
#Entity
#Cacheable(false)
public class PaymentRecordT {
...
#ManyToOne
#PrimaryKeyJoinColumn(name = "payment_status_cd", referencedColumnName = "REC_STAT_CD")
private RecordStatusT recordStatusT;
}
I still feel like there should be a better solution. Eclipselink tracks the inserts/updates so it should be able track what needs rereading from the DB and what not. I still feel like I don't fully understand the entire picture, but this works for me and its reasonably clean.
I can leave the considerable amount of read-only data/objects chacheable and the few that are changeable as non-cacheable.
Thanks for reading
Carsten

Mapping one DB column to two seperate fields using JPA

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.

Handling NULL values in #DiscriminatorColumn

I have a legacy MS SQL Server 2000 datatabase I am trying to map with JPA (EclipseLink to be precise).
I have a table "CONTACT1" with a text field "KEY1" which can be either 'Contact' or 'Asset', and I am using:
#Table(name = "CONTACT1")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "KEY1")
public abstract class Record {
to map it to two different classes (Contact and Asset).
#Entity
#DiscriminatorValue(value = "Contact")
public class Contact extends Record {...}
and
#Entity
#DiscriminatorValue(value = "Asset")
public class Asset extends Record {...}
Unfortunately I found out some or the records are missing, and after further analysis I realised some contact records have the "KEY1" field set to NULL.
While I could just run an update query to fix the problem I would like to avoid that - is there a way to map that 'NULL' value to a Contact class?
Failing that, is there a mechanism in JPA where I can pre-filter the recordsets making JPA believe that field is not NULL but it has 'Contact' written in it?
You need to run the update and assign correct values to the rows with null values in the database. I also advice you to modify the column definition in the database and set a default value for it, to prevent null in the future.
Or don't use #DiscriminatorColumn. In my experience, it tends to add more complexity and bugs than it's worth.
Just a clarification : I found that if you set a #DiscriminatorColumn annotation on any column (e.g.KEY1 here), Hibernate ORM creates a database schema with a NOT NULL constraint. Since your legacy DB has null values maybe an ALTER table to make it NULLABLE has been issued later.
Details in : https://hibernate.atlassian.net/browse/HHH-12445
Solution 1:
You can using Discriminator formula to map the null values to the preferred entity class.
#DiscriminatorFormula("case when key1 is null then 'Contact' else key1 end)
#DiscriminatorValue(value = "Contact")
Note Example 247 in https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html
Solution:2
You can also use #DiscriminatorColumn("null") to map that row to "Contact" entity class.
Example 254 in
https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#entity-inheritance-discriminator-implicit

JPA composite key #OneToMany

I have the following existing DB schema, which I'd like to recreate with Java and plain JPA annotations (using hibernate as provider, so hibernate specific annotations would work as a last resort):
CREATE TABLE users (
user_id NUMBER NOT NULL -- pk
);
CREATE TABLE userdata_keys (
userdata_key_id NUMBER NOT NULL, -- pk
key VARCHAR2(128) NOT NULL
);
CREATE TABLE users_userdata (
user_id NUMBER NOT NULL, -- fk users.user_id
userdata_key_id NUMBER NOT NULL, -- fk userdata_keys.userdata_key_id
value VARCHAR2(256)
);
I've thus created the following classes and annotations:
class User {
#Id
Long id;
#OneToMany
Set<Userdata> userdata;
}
class UserdataKey {
#Id
Long id;
String key;
}
class Userdata {
String value;
#EmbeddedId
UserdataId userdataId;
}
#Embeddable
class UserdataId {
User user;
UserdataKey userdataKey;
}
I left out columnName attributes and other attributes of the entities here.
It does however not quite work as intended. If I do not specify a mappedBy attribute for User.userdata, hibernate will automatically create a table USERS_USERS_USERDATA, but as far as I've seen does not use it. It does however use the table which I specified for the Userdata class.
Since I'm rather new to Java and hibernate as well, all I do to test this currently is looking at the DB schema hibernate creates when persisting a few sample entries.
As a result, I'm entirely puzzled as to whether I'm doing this the right way at all. I read the hibernate documentation and quite a bunch of Google results, but none of them seemed to deal with what I want to do (composite key with "subclasses" with their own primary key).
The mappedBy attribute is mandatory at one of the sides of every bidirectional association. When the association is a one-to-many, the mappedBy attribute is placed ot the one- side (i.e. on the User's userdata field in your case).
That's because when an association is bidirectional, one side of the association is always the inverse of the other, so there's no need to tell twice to Hibernate how the association is mapped (i.e. which join column or join table to use).
If you're ready to recreate the schema, I would do it right (and easier), and use a surrogate auto-generated key in users_userdata rather than a composite one. This will be much easier to handle, in all the layers of your application.

Why Hibernates ignores the name attribute of the #Column annotation?

Using Hibernate 3.3.1 and Hibernate Annotations 3.4, the database is DB2/400 V6R1, running that on WebSphere 7.0.0.9
I have the following class
#Entity
public class Ciinvhd implements Serializable {
#Id
private String ihinse;
#Id
#Column(name="IHINV#")
private BigDecimal ihinv;
....
}
For reasons I can't figure, Hibernate ignores the specified column name and uses 'ihinv' to generate the SQL:
select
ciinvhd0_.ihinse as ihinse13_,
ciinvhd0_.ihinv as ihinv13_,
...
Which of course gives me the following error:
Column IHINV not in table CIINVHD
Edit: I switched the log level of hibernate to DEBUG, and I see that it does not process the column annotation for that field. Tried several random things it just doesn't work.
Did anyone had this problem before? I have other entities that are very alike in the way that they are using # in their database field names and that are part of the PK and I don't have this problem with them.
You could try some kind of quoting:
For example:
#Column(name="`IHINV#`")
or
#Column(name="'IHINV#'")
Another option would be to dig in to source code Hibernate dialect for DB2 and see if it contains anything helpful.
Of course, the easiest way would be to remove the hash from column name if possible.
I suspect that the problem is the hash in the column name. A similar question on the hibernate forums suggests that backticks can be useful here.

Categories

Resources