There exists a proprietary hibernate annotation to specify the Foreign Key constraint names that are used at DDL generation time: org.hibernate.annotations.ForeignKey.
Is there also a way to specify the Primary Key constraint names?
Not possible with standard JPA and not supported by Hibernate for Primary Key constraints neither.
There is actually a very old issue about this feature request (HB-1245) but it looks like it doesn't get much attention.
You can control the generated PK constraint names with a few small mods in a custom dialect. For example, here's how to do it in Oracle (the same approach works for SQLServer & DB2):
public class CustomOracleDialect extends org.hibernate.dialect.Oracle10gDialect {
private CustomTableExporter customTableExporter;
public CustomOracleDialect () {
super();
customTableExporter = new CustomTableExporter(this);
}
#Override
public Exporter<Table> getTableExporter () {
return customTableExporter;
}
static class CustomTableExporter extends StandardTableExporter {
private final static int MAX_TABLE_NAME_LENGTH = 30;
public CustomTableExporter (Dialect dialect) {
super(dialect);
}
#Override
public String[] getSqlCreateStrings (Table table, Metadata metadata) {
final String[] sqlCreateStrings = super.getSqlCreateStrings(table, metadata);
//-- replace " primary key" with " constraint TABLE_NAME_PK primary key "
final String namedPkConstraint = " constraint " + StringUtils.truncate(table.getName(), MAX_TABLE_NAME_LENGTH - 3) + "_PK primary key ";
for (int i = 0; i < sqlCreateStrings.length; ++i) {
sqlCreateStrings[i] = StringUtils.replace(sqlCreateStrings[i], " primary key ", namedPkConstraint);
}
return sqlCreateStrings;
}
}
}
This will change the generated DDL from this:
-- BEFORE:
create table FOO_ENTITY (
FOO_ENTITY_ID number(19, 0) not null,
JOB_NAME varchar2(128 char) not null,
primary key (FOO_ENTITY_ID)
);
To this :
-- AFTER:
create table FOO_ENTITY (
FOO_ENTITY_ID number(19, 0) not null,
JOB_NAME varchar2(128 char) not null,
constraint FOO_ENTITY_PK primary key (FOO_ENTITY_ID)
);
The class org.hibernate.mapping.PrimaryKey does the following:
public String sqlConstraintString(Dialect dialect) {
StringBuilder buf = new StringBuilder("primary key (");
Iterator iter = getColumnIterator();
while ( iter.hasNext() ) {
buf.append( ( (Column) iter.next() ).getQuotedName(dialect) );
if ( iter.hasNext() ) {
buf.append(", ");
}
}
return buf.append(')').toString();
}
The solution would be to override this method and return a string starting with "constraint YOUR_CONSTRAINT_NAME primary key" to make it possible. Unfortunately there's no way of overriding this.
If you're talking about choosing the name of your primary key (in the database), Hibernate can not do that.
Remember, Hibernate is a framework that is primarly focused on mapping objects, not on the creation/maintenance of database entities.
With regards to defining the primary key, the following link (particularly 2.2.3.2) might be helpful: Mapping identifier properties in the JBoss Hibernate guide
Related
Have the following table and Java Entity:
CREATE TABLE search_terms (
id int(100) NOT NULL AUTO_INCREMENT,
term varchar(255) NOT NULL DEFAULT '',
last_search_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
search_count int(100) NOT NULL DEFAULT '0',
user_email varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
public class SearchTerms implements Serializable {
private Long id;
private String term;
private Timestamp lastSearchDate;
private int searchCount;
private String userEmail;
Want to persist java objects of the given type into the table above.
Example:
List<String> searchTerms = Arrays.asList("test1", "test2", "test3");
saveSearchParams(searchTerms);
If any of those terms exist in the table, I want to increment searchCount else save as a new row.
Need to use JPA.em().merge(o) etc and not have sql insert/update queries
Added the following constant on the two columns but JPA.em().merge(o) keeps inserting new rows.
#Table(name="search_terms", uniqueConstraints= { #UniqueConstraint(columnNames = {"term", "user_email"})})
saveSearchParams() {
searchTerms.forEach(o -> {
SearchTerms term = new SearchTerms();
term.setSearchCount(1);
term.setTerm((String) o);
term.setUserEmail(email);
jpaApi.em().merge(term);
});
}
Any help on or documentation is appreciated.
Merge can both update and insert, but you should check if the object exists in DB to see if you have to set the counter to one or add one to it, for this you will have to throw a query for each element.
try this:
saveSearchParams() {
searchTerms.forEach(o -> {
//search for example by id or in any other way
SearchTerms term = jpaApi.em().find(SearchTerms.class, o.getId());
if (term == null){
term = new SearchTerms();
term.setSearchCount(1);
}else{
term.setSearchCount(term.getSearchCount()+1);
}
term.setTerm((String) o);
term.setUserEmail(email);
jpaApi.em().merge(term);
});
}
I'm very new to using databases and SQL in general and I'm having some trouble figuring out a function that will allow me to display records from a table in my jdbc database based on data from other tables in the database. I will illustrate below:
Example of "DEMANDS" table (column headers, "ID" is the primary key):
NAME|ADDRESS|DESTINATION|DATE|TIME|ID
Example of "DRIVERS" table ("REGISTRATION" is the primary key):
USERNAME|PASSWORD|REGISTRATION|NAME
Example of "JOURNEY" table ("JID" is the primary key,"REGISTRATION" is a foreign key)
JID|NAME|ADDRESS|DESTINATION|DISTANCE|REGISTRATION|DATE|TIME|STATUS
Below is the code that I have that is used to display tables on a jsp file:
public String retrieve(String query) throws SQLException {
select(query);
return makeTable(rsToList());//results;
}
private void select(String query){
try {
statement = connection.createStatement();
rs = statement.executeQuery(query);
//statement.close();
}
catch(SQLException e) {
System.out.println("way way"+e);
//results = e.toString();
}
}
private String makeTable(ArrayList list) {
StringBuilder b = new StringBuilder();
String[] row;
b.append("<table border=\"3\">");
for (Object s : list) {
b.append("<tr>");
row = (String[]) s;
for (String row1 : row) {
b.append("<td>");
b.append(row1);
b.append("</td>");
}
b.append("</tr>\n");
} // for
b.append("</table>");
return b.toString();
}//makeHtmlTable
private ArrayList rsToList() throws SQLException {
ArrayList aList = new ArrayList();
ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount(); //number of column
String columnName[] = new String[count];
for (int i = 1; i <= count; i++)
{
columnName[i-1] = metaData.getColumnLabel(i);
}
aList.add(columnName);
int cols = rs.getMetaData().getColumnCount();
while (rs.next()) {
String[] s = new String[cols];
for (int i = 1; i <= cols; i++) {
s[i-1] = rs.getString(i);
}
aList.add(s);
} // while
return aList;
} //rsToList
All of this code works fine and if I pass in a query into the "Retrieve" function such as:
String query = "select * from DRIVERS";
It will display all of the records of the "DRIVERS" table.
What I am wanting to do though, is only list drivers from the driver table that are available at the time specified in the demand (meaning their registration is not currently in a record in the journey table at the same time as the demand) If possible, I would also only like to display the "NAME" and "REGISTRATION" columns as oppose to the whole record.
I would really appreciate some help with this as I've searched around for solutions for quite some time and have not been able to work out a function that will achieve the desired outcome.
Cheers,
Creation of tables script:
-- --------------------------------------------------------
--DROP Table Demands;
CREATE TABLE Demands (
Name varchar(20),
Address varchar(60),
Destination varchar(60),
Date date DEFAULT NULL,
Time time DEFAULT NULL,
Status varchar(15) NOT NULL,
id INT primary key
);
-- --------------------------------------------------------
--DROP Table Drivers;
CREATE TABLE Drivers (
username varchar(20),
password varchar(20),
Registration varchar(10),
Name varchar(20),
PRIMARY KEY (Registration)
);
-- --------------------------------------------------------
--DROP Table Journey;
CREATE TABLE Journey (
jid INT primary key
Destination varchar(60),
Distance integer NOT NULL DEFAULT 1,
Registration varchar(10) NOT NULL,
Date date NOT NULL,
Time time DEFAULT NULL
);
The following query may answer your question.
SELECT Drivers.Name, Drivers.Registration
FROM Drivers
LEFT JOIN Journey ON Journey.Registration = Drivers.Registration
LEFT JOIN Demands ON Demands.Date = Journey.Date
WHERE Demands.id IS NULL;
This joins JOURNEY and DRIVER based on the foreign key relation. It then outer-joins DEMANDS and JOURNEY based on an implicit relation that is DATE. Finally we only keep records that fail the outer join condition.
The model has a major flaw though as the relation between DEMANDS and JOURNEY is based on a field of type Date, as far as one can tell by what your provided.
When I enter data with my java program (simple dictionary ) it throws an error:
MySQLIntegrityConstraintViolationException: Cannot add or update a
child row: a foreign key constraint fails (singlehaw.card,
CONSTRAINT card_ibfk_1 FOREIGN KEY (wordId) REFERENCES word
(wordId))
But when I enter data through query in command prompt I don't face any problem.
here I post my method:
public boolean insert(Card card) {
Connection connection = MySqlUtils.getInstance().getConnection();
PreparedStatement statement = null;
ResultSet resultSet = null;
int cardId = -1;
try {
String INSERT_INTO_TABLE_CARD_QUERY = "INSERT INTO "
+ TBL_CARD + " ("
+ STATUS + ", "
+ RATING + ", "
+ INSERT_TIME + ", "
+ DIC_ID + ", "
+ WORD_ID
+ ") VALUES (?,?,NOW(),?,?)";
statement = connection.prepareStatement(INSERT_INTO_TABLE_WORDS_QUERY, Statement.RETURN_GENERATED_KEYS);
statement.setString(1, card.getStatus().name());
statement.setInt(2, card.getRating());
statement.setInt(3, card.getDictionaryId());
statement.setInt(4, card.getWordId());
statement.execute();
// get last inserted id
resultSet = statement.getGeneratedKeys();
if (resultSet.next())
cardId = resultSet.getInt(1);
} catch (SQLException e) {
e.printStackTrace();
return false;
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
card.setCardId(cardId);
return true;
}
and also scripts of creating tables:
CREATE TABLE dictionary (
dictionaryId SERIAL,
dictionary VARCHAR(128) NOT NULL,
PRIMARY KEY (dictionaryId)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE word (
wordId SERIAL,
word VARCHAR(255) NOT NULL UNIQUE,
transcription VARCHAR(255),
PRIMARY KEY (wordId)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE card (
cardId SERIAL,
status ENUM ('EDIT', 'POSTPONED', 'TO_LEARN', 'LEARNT') NOT NULL DEFAULT 'TO_LEARN',
rating TINYINT DEFAULT '0' NOT NULL,
insert_time TIMESTAMP DEFAULT NOW(),
update_time TIMESTAMP,
dictionaryId BIGINT UNSIGNED NOT NULL,
wordId BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (cardId),
FOREIGN KEY (wordId) REFERENCES word (wordId),
FOREIGN KEY (dictionaryId) REFERENCES dictionary (dictionaryId) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Perhaps, the fact your wordID fields on the tables have different data types is affecting your program. SERIAL is an alias for Bigint. Idea discarded.
Print somwehere in the logs the actual statement being executed. Maybe there's something that's not being included.
Thnx guys a lot. Understood many things from this topic. Right now the problem has gone. The problem was due to populating tables via JUnit tests and because of maven my tests gone in a wrong order so it was difficult to recognize the real problem.
You can switch off your constraints ,execute your query, and switch constraints on.
SET FOREIGN_KEY_CHECKS=0;
... here is your sql ...
SET FOREIGN_KEY_CHECKS=1;
My database had a lot of parent and child tables.The tables contains the foreign key which has the link with the parent table.I wants to get the information of parent table of the child table using java?How can I achieve that?
For ex,consider the student and mark table,
The student table contains the information like studentID,name.
studentID-Primary key
The marks table contains the markId,studentId,Sub1,sub2,sub3 etc
markId-Primarykey
studentID-Foreignkey refers Student table
My table creation queries are,
CREATE TABLE `Student12` (
`studentId` SMALLINT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`studentId`)
)
ENGINE = InnoDB;
CREATE TABLE `Marks` (
`markId` SMALLINT NOT NULL AUTO_INCREMENT,
`subject1` SMALLINT NOT NULL,
`subject2` SMALLINT NOT NULL,
`studentId` SMALLINT NOT NULL,
PRIMARY KEY (`markId`),
CONSTRAINT `FK_Marks_Student` FOREIGN KEY `FK_Marks_Student` (`studentId`)
REFERENCES `Student12` (`studentId`)
ON DELETE RESTRICT
ON UPDATE RESTRICT
)
ENGINE = InnoDB;
If I give the mark table name as input, how can I get its parent or super table name student and information about student table?Any help should be appreciable.
It totally depends on the way tables are created. Foreign keys are not mandatory to create, they could be a simple column in one table with no explicit relationship to the other table. If you are very sure that the links are created explicitly (the foreign keys are defined) then you could use information_schema. But if there is no foreign key defined (which is true in most of the databases I have seen), then there is no way for you to find the links inside the database. You have to look into the code (if there is any available) and try to find a clue.
The JDBC DatasetMetaData interface provides a couple of methods that may help. (The following text is copied from the javadoc.
ResultSet getExportedKeys(String catalog, String schema, String table)
Retrieves a description of the foreign key columns that reference the given table's primary key columns (the foreign keys exported by a table).
ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog, String foreignSchema, String foreignTable)
Retrieves a description of the foreign key columns in the given foreign key table that reference the primary key or the columns representing a unique constraint of the parent table (could be the same or a different table).
Of course, these can only work if the relevant columns have been declared as foreign keys in the SQL table DDL.
You can use the DatabaseMetaData to retrieve informations about foreign keyes
and the referenced Tables. Im not sure if it works with all kinds of MySql Tables.
The principle is to use the follwing code (not tested) to retrieve information about the super tables
ResultSet rs = null;
DatabaseMetaData dm = conn.getMetaData( );
// get super tables of table marks
ResultSet rs = dm.getSuperTables( null , null, "marks" );
while( rs.next( ) ) {
System.out.println(String.format("Table Catalog %s", rs.getString("TABLE_CAT") );
System.out.println(String.format("Table Schema %s", rs.getString("TABLE_SCHEM") );
System.out.println(String.format("Table Name %s", rs.getString("TABLE_NAME") );
System.out.println(String.format("Table Name %s", rs.getString("SUPERTABLE_NAME") );
}
You can use thes informations to get additional informations about the referenced table
and the foreigen and referenced primary keys:
ResultSet rs = dm.getCrossReference( null , null , "student" , null , null , "marks" );
System.out.println(String.format("Exported Keys Info Table %s.", "marks"));
while( rs.next( ) ) {
String pkey = rs.getString("PKCOLUMN_NAME");
String ptab = rs.getString("PKTABLE_NAME");
String fkey = rs.getString("FKCOLUMN_NAME");
String ftab = rs.getString("FKTABLE_NAME");
System.out.println("primary key table = " + ptab);
System.out.println("primary key = " + pkey);
System.out.println("foreign key table = " + ftab);
System.out.println("foreign key = " + fkey);
}
And finally you can retrieve the information about the super table by
ResultSet rs = dm.getTables(null,null,"student" ,null);
System.out.println("Table name:");
while (rs.next()){
String table = rs.getString("TABLE_NAME");
System.out.println(table);
}
I have a small local H2 database whose content has been create with DataNucleus' JDO implementation. It contains a rawcontainitem table, associated to the following object:
#PersistenceCapable(objectIdClass=RawItemKey.class)
#Index(name="CONTAIN_IDX", members={"prefix", "language", "value"})
public class RawContainItem {
#PrimaryKey
#Column(length=40)
String prefix = "";
#PrimaryKey
#Column(length=2)
String language = "";
#PrimaryKey
#Column(length=Integer.MAX_VALUE)
String value = "";
public RawContainItem(String prefix, String language, String value) {
this.prefix = prefix;
this.language = language;
this.value = value;
}
}
This table currently contains several rows where language="FR". I want to add some more rows with language="EN", but I get strange error messages.
When datanucleus.autoCreateSchema is set to true, I get:
INFO: Managing Persistence of Class : net.dwst.findword.DataNucleus.RawContainItem [Table : RAWCONTAINITEM, InheritanceStrategy : new-table]
23-mars-2012 14:42:49 org.datanucleus.store.rdbms.table.AbstractTable create
INFO: Creating table RAWCONTAINITEM
23-mars-2012 14:42:50 org.datanucleus.store.rdbms.table.AbstractTable executeDdlStatementList
GRAVE: Error thrown executing CREATE TABLE RAWCONTAINITEM
(
"LANGUAGE" VARCHAR(2) NOT NULL,
PREFIX VARCHAR(40) NOT NULL,
"VALUE" VARCHAR(2147483647) NOT NULL,
CONSTRAINT RAWCONTAINITEM_PK PRIMARY KEY ("LANGUAGE",PREFIX,"VALUE")
) : Table "RAWCONTAINITEM" already exists; SQL statement:
CREATE TABLE RAWCONTAINITEM
(
"LANGUAGE" VARCHAR(2) NOT NULL,
PREFIX VARCHAR(40) NOT NULL,
"VALUE" VARCHAR(2147483647) NOT NULL,
CONSTRAINT RAWCONTAINITEM_PK PRIMARY KEY ("LANGUAGE",PREFIX,"VALUE")
) [42101-164]
org.h2.jdbc.JdbcSQLException: Table "RAWCONTAINITEM" already exists;
...
This message is true, the table exists indeed.
When datanucleus.autoCreateSchema is set to false, I get:
Required table missing : "RAWCONTAINITEM" in Catalog "" Schema "".
DataNucleus requires this table to perform its persistence operations.
Either your MetaData is incorrect, or you need to enable "datanucleus.autoCreateTables"
org.datanucleus.store.rdbms.exceptions.MissingTableException:
Required table missing : "RAWCONTAINITEM" in Catalog "" Schema "".
DataNucleus requires this table to perform its persistence operations.
Either your MetaData is incorrect, or you need to enable "datanucleus.autoCreateTables"
...
But the table does exists... What gives?