JPA/Hibernate - Use unique constraint as primary key - java

I have the following problem, I have a already existing table with three fields field1, field2, field3. Field1 is actually a foreign key (#OneToOne) to another table. All field2 and field3 can be null, so I can't set a primary key for all three fields. In the database there is a UniqueConstraint for field1+field2+field3.
field1 | field2 | field 3
1 | 1 | null
1 | null | 2
3 | 1 | null
I've tried several solutions with JPA/Hibernate but could not find a good one.
Without defining a #Id JPA can not work (of course). Defining the three fields in a #Embeddable and reuse it with #EmbeddedId creates a primary key over all three fields which do not allow null values.
Is there any other solution than change the existing table and add a auto generated id for each row?
Thank you so much & best regards!

Have you tried as below?
#Table(
name="xxx",
uniqueConstraints=
#UniqueConstraint(name="my_unique_key", columnNames={"field1", "field2", "field3"})
)

Related

Remove zombie foreign keys in MYSQL

I'm trying to build a MySQL database gradually, by generation using JPA and Eclipse-Link. Along the way, I've changed some relationships #ManyToOne, #OneToOne etc.
I now have a situation where I have some spurious foreign keys: the tables don't exist, but the referenced tables still do. I think the original tables were cross-reference tables generated by EclipseLink but are no longer around.
The issue is, I cannot delete these referenced tables. I get an error like this:
mysql> drop table PRODUCTDO;
ERROR 3730 (HY000): Cannot drop table 'PRODUCTDO' referenced by a foreign key constraint 'PRODUCTDO_DISTRIBUTIONCENTERPRODUCTDO_ProductDo_ID' on table 'PRODUCTDO_DISTRIBUTIONCENTERPRODUCTDO'.
If I run:
SET foreign_key_checks = 0;
then I can delete the table, but the constraint still remains. Even if I drop the database and create it again, the constraint is still there:
mysql> SELECT TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = 'SCO';
+---------------------------------------+------------------------------+----------------------------------------------------+-----------------------------+------------------------+
| TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME | REFERENCED_TABLE_NAME | REFERENCED_COLUMN_NAME |
+---------------------------------------+------------------------------+----------------------------------------------------+-----------------------------+------------------------+
| PRODUCTDO_DISTRIBUTIONCENTERPRODUCTDO | ProductDo_ID | PRODUCTDO_DISTRIBUTIONCENTERPRODUCTDO_ProductDo_ID | PRODUCTDO | ID |
| PRODUCTDO_DISTRIBUTIONCENTERPRODUCTDO | distributionCenterProduct_ID | PRDCTDDSTRBTIONCENTERPRODUCTDOdstrbtnCntrProductID | DISTRIBUTIONCENTERPRODUCTDO | ID |
+---------------------------------------+------------------------------+----------------------------------------------------+-----------------------------+------------------------+
2 rows in set (0.01 sec)
How can I get rid of these zombie constraints?
In the end, I had to recreate the table and foreign key then I was able to delete it.

Hibernate JoinTable error: Unable to find column with logical name

I am trying to do a Unidirectional ManyToOne association via a JoinTable in hibernate, but I keep getting the following error:
A JPA error occurred (Unable to build EntityManagerFactory): Unable to find column with logical name: name in org.hibernate.mapping.Table(users) and its related supertables and secondary tables
I have 3 models House, User, UserHouseMap. I want to be able to access a users house through the UserHouseMap. Here is the mapping on the User
For other reasons, I need to map a User to UserHouseMap via a column that is not a primary key
#Id
#GeneratedValue
#Expose
public Long id;
#Expose
#Required
#ManyToOne
#JoinTable(name = "user_house_map",
joinColumns= {#JoinColumn(table="users", name="user_name", referencedColumnName="name")},
inverseJoinColumns={#JoinColumn(table="houses", name="house_name", referencedColumnName="house_name")})
public House house;
Here are the DB schemas for all 3 models
Users
Table "public.users"
Column | Type | Modifiers
-----------------------+-----------------------------+-----------------------------
name | character varying(255) |
id | integer | not null
Indexes:
"user_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"housing_fkey" FOREIGN KEY (name) REFERENCES user_house_map(user_name) DEFERRABLE INITIALLY DEFERRED
Houses
Table "public.houses"
Column | Type | Modifiers
---------------+------------------------+-----------
house_name | character varying(255) | not null
address | text |
city | text |
state | text |
zip | integer |
zip_ext | integer |
phone | text |
Indexes:
"house_pkey" PRIMARY KEY, btree (house_name)
Referenced by:
TABLE "user_house_map" CONSTRAINT "house_map_fkey" FOREIGN KEY (house_name) REFERENCES house(house_name) DEFERRABLE INITIALLY DEFERRED
UserHouseMap
Table "public.user_house_map"
Column | Type | Modifiers
-------------+------------------------+-----------
user_name | character varying(255) | not null
house_name | character varying(255) | not null
Indexes:
"user_house_map_pkey" PRIMARY KEY, btree (user_name)
"user_house_map_house_key" btree (house_name)
Foreign-key constraints:
"user_house_map_house_fkey" FOREIGN KEY (house_name) REFERENCES houses(house_name) DEFERRABLE INITIALLY DEFERRED
Referenced by:
TABLE "users" CONSTRAINT "housing_fkey" FOREIGN KEY (name) REFERENCES user_house_map(user_name) DEFERRABLE INITIALLY DEFERRED
Remove table="users" and table="houses" from the JoinColumn annotations.
This attribute should not contain the target table of the foreign key. It's only used when an entity is mapped to two tables, in order to tell which table the join column is in.
Also, your table definitions are quite strange:
the join table should have two foreign keys: one referencing the PK of the users table, and one referencing the PK of the house table. Why do you reference the name of a user rather than its ID?
the foreign key constraint on users.name doesn't make sense.

How to do a custom database setup/teardown in Spring Test Dbunit?

I would like to know how to create custom setups/teardown mostly to fix cyclyc refence issues where I can insert custom SQL commands with Spring Test Dbunit http://springtestdbunit.github.io/spring-test-dbunit/index.html.
Is there an annotation I can use or how can this be customized?
There isn't currently an annotation that you can use but you might be able to create a subclass of DbUnitTestExecutionListener and add custom logic in the beforeTestMethod. Alternatively you might get away with creating your own TestExecutionListener and just ordering it before DbUnitTestExecutionListener.
Another, potentially better solution would be to re-design your database to remove the cycle. You could probably drop the reference from company to company_config and add a unique index to company_id in the company_config table:
+------------+ 1 0..1 +--------------------------------+
| company |<---------| company_config |
+------------+ +--------------------------------+
| company_id | | config_id |
| ... | | company_id (fk, notnull, uniq) |
+------------+ +--------------------------------+
Rather than looking at company.config_id to get the config you would do select * from company_config where company_id = :id.
Dbunit needs the insert statements (xml lines) in order, because they are performed sequentially. There is no or magic parameter or annotation so dbunit can resolve your cyclyc refences or foreign keys automatically.
The most automate way I could achieve if you your data set contain many tables with foreign keys:
Populate your database with few records. In your example: Company, CompanyConfig and make it sure that the foreign keys are met.
Extract a sample of your database using dbunit Export tool.
This is an snippets you could use:
IDatabaseConnection connection = new DatabaseConnection(conn, schema);
configConnection((DatabaseConnection) connection);
// dependent tables database export: export table X and all tables that have a // PK which is a FK on X, in the right order for insertion
String[] depTableNames = TablesDependencyHelper.getAllDependentTables(connection, "company");
IDataSet depDataset = connection.createDataSet(depTableNames);
FlatXmlWriter datasetWriter = new FlatXmlWriter(new FileOutputStream("target/dependents.xml"));
datasetWriter.write(depDataset);
After running this code, you will have your dbunit data set in "dependents.xml", with all your cycle references fixed.
Here I pasted you the full code: also have a look on dbunit doc about how to export data.

How Session.get method works in hibernate

I am trying to understand that how object initialization works for returned object by Session Get method.Please validate my understanding. When it executes, it checks for object with given identifier in the first level cache and then the Second level cache (If it is configured), If not found then fires the select query to retrieve the data from database.
My question is, Does it include associations in select query which are configured for lazy loading or null value is set for such associations in returned object?
If this is case then session.get does not do the complete initialization of the returned object which is contradictory to what is written on most of hibernate tutorials available on web.
Hibernate Session provide different methods to fetch data from database. Two of them are – get() and load().
get() returns the object by fetching it from database or from hibernate cache.
when we use get() to retrieve data that doesn’t exists, it returns null, because it try to load the data as soon as it’s called.
We should use get() when we want to make sure data exists in the database.
For Example :
In a Stock application , Stock and StockTransactions should have a “one-to-many” relationship, when you want to save a stock transaction, it’s common to declared something like below.
Stock stock = (Stock)session.get(Stock.class, new Integer(2));
StockTransaction stockTransactions = new StockTransaction();
//set stockTransactions detail
stockTransactions.setStock(stock);
session.save(stockTransactions);
Output :
Hibernate:
select ... from mkyong.stock stock0_
where stock0_.STOCK_ID=?
Hibernate:
insert into mkyong.stock_transaction (...)
values (?, ?, ?, ?, ?, ?)
In session.get(), Hibernate will hit the database to retrieve the Stock object and put it as a reference to StockTransaction.
To answer the question:
Does it include associations in select query which are configured for lazy loading or null value is set for such associations in returned object?
1) The session.get() will NOT initiate lazy stuff. NEVER. In fact that is the central thought of the design. Otherwise - we would be able to load whole DB in one SHOT (in one JAVA call to session.get())
2) And also there WILL NOT be null instead. Each reference or collection will be represented by proxy. This is the way how we can avoid to load compelte DB in one shot (all stuff initialized with one method get). Because each proxy is in fact a promise - once we will touch it... it will load the real data.
And so on. So get is very safe way how to recieve as few data as was configured....
Simply
When get() method is called, it will directly hit the database, fetch the result and return. If no matching fields are found, it will gladly return null.
Depending on the annotations on references, Lazy or Eager, data will be returned. if Lazy, proxy will be returned instead of null, if Eager, fully initialized object will be returned.
Better to monitor the queries at the backend, for good understanding.
1) Customer entity class that map the T_CUSTOMER DB table:
#Entity
#Table(name= “T_CUSTOMER”)
public class Customer {
#Id
#Column (name=“cust_id”)
private Long id;
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn (name=“cid”)
private Set<Address> addresses;
…
…
…
}
2) Address entity class that map the T_ADDRESS DB table:
#Entity
#Table(name= “T_ADDRESS”)
public class Address {
// Fields and Properties
}
Consider this Customers table :
----------------------------------------------------------------------------
| Cust_id | Cust_firstname | Cust_lastname | Cust_email | Cust_mobile |
----------------------------------------------------------------------------
| 101 | XXXX | YYYYY |xxx#xyz.com | 8282263131 |
----------------------------------------------------------------------------
The Above customers table is having one record with cust_id as 101.
Now Consider this Address Table :
----------------------------------------------------------------------------
| id | street | suburb | city | zipcode | cid |
----------------------------------------------------------------------------
| 1 | streetX | AreaY | cityZ | 54726 | 101 |
----------------------------------------------------------------------------
| 2 | streetXA | AreaYB | cityZS | 60660 | 101 |
----------------------------------------------------------------------------
Now When you invoke :
Customer cust = (Customer)session.get(Customer.class, 101);
Then Hibernate will fire a SQL Query Something like :
1). In case of EAGER LOADING :
SELECT * FROM T_CUSTOMER cust JOIN T_ADDRESS add ON cust.cust_id=add.cid
i.e, It will load all the data related to the T_CUSTOMERS table and it's associated tables, which is T_ADDRESS table in this case.
2). I case of LAZY LOADING :
SELECT * FROM T_CUSTOMER WHERE cust_id=101;
So, it only fetches the data corresponding to the T_CUSTOMER table and uses Proxy for the T_ADDRESS table as said above by #Radim Köhler. It will fetch the data from the T_ADDRESS TABLE only when you'll call :
cust.getAddresses();

Hibernate Mapping One-to-many in one table

We have the following (poorly designed?) table:
inputs:
keyword_id serial not null,
group_name string not null,
banned_term string not null
Keyword ID is the primary key. there are many banned terms per group_name. The data looks like this:
keyword_id | group_name | banned_term
1 | incentivization | free money
2 | inaccuracy | we're number one
3 | incentivization | win a free ipod!
There's no join table, and group_name isn't its own entity. I'd like a domain object something like this:
class BannedTermGroup {
Integer id;
String group_name;
Set<String> banned_terms;
// ... various getters and setters
}
The only examples on this one-to-many relationship between the group name and banned terms all involve some sort of join column or join table, while group_name would always be part of some other entity. Here neither is the case. Can this be mapped using Hibernate?
As quoted in my previous comment, you can get the ID of the group from nowhere.
Just to put aside this problem, you may look into Hibernate's reference, search for collections of basic types. It is what you need.
However, you still need to have a table for the "group".
assume it is as simple as a one column table:
KEYWORD_GROUP
--------------------
GROUP_NAME STRING
Your original keyword table:
KEYWORD
--------------------
GROUP_NAME STRING
KEYWORD STRING
I believe this is what you need:
#Entity
#Table("KEYWORD_GROUP")
public class KeywordGroup {
[.....]
#ElementCollection
#CollectionTable(name="KEYWORD", joinColumns=#JoinColumn(name="GROUP_NAME"))
#Column(name="KEYWORD")
private List<String> keywords;
}
For the keyword_group table, you probably don't need to create a new table if you are just using this model for read. Create a view from your original keyword table should be good enough.

Categories