# [JPA] Frage zu @ElementCollection



## dmike (16. Aug 2011)

Meine Frage: ich schreib gerade einen Testcase. Im TestCase möchte ich die Klasse  Event gegen eine inmemory hsql db testen. Bevor der Testcase startet habe ich in der mit @BeforeTransaction  (aus Spring) annotierten setup Methode folgende SQL Code laufen um die Tabellen zu füllen:




```
INSERT INTO event (id, creationtimestamp.....);

INSERT INTO I18NSTRING (id, I18NSSTRING_LANGUAGE_CODE, I18NSSTRING_CONTENT) values (1, 'de', 'EVENT 1 (TEST)');
INSERT INTO I18NSTRING (id, I18NSSTRING_LANGUAGE_CODE, I18NSSTRING_CONTENT) values (1, 'en', 'EVENT 2 (TEST)');
```


Dabei fliegt mir eine  


```
org.hsqldb.HsqlException: integrity constraint violation: foreign key no parent; FK73DAB3AF4E0EC202 table: I18NSTRING
```

Exception um die Ohren.

Kann ich nun in Tabellen, die über @ElementCollection angelegt wurden eigentlich vorab per SQL INSERTS füllen? (Ich weiß ja nicht wie genau Hibernate die Tabelle anlegt, welche Contraints beachtet werden müsse, etc.)

Hier also der Code

1. Klasse Event mit dem I18Strings Object, das embedded werden soll. 


```
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Event extends AbstractPersistenceEntity {

    @Embedded
    private I18NStrings eventTitles = new I18NStrings();

}
```

2. Die Klasse I18Strings. Im Prinzip verwaltet sie nur eine Collection von I18String Objekten.


```
@Embeddable
public class I18NStrings {
    // [url=http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection]Java Persistence/ElementCollection - Wikibooks, open books for an open world[/url]
    @ElementCollection
    @CollectionTable(
          name="I18NSTRING",
          joinColumns=@JoinColumn(name="id")
    )
    @Column(name="I18NSTRING")
    private Set<I18NString> I18NStrings = new HashSet<I18NString>();
}
```


3. Die I18String Klasse. Ist im Prinzip nur ein String mit einer Locale


```
@Embeddable
public class I18NString {

    @Column(name = "I18NSSTRING_CONTENT", nullable = false)
    private String string;

    @Column(name = "I18NSSTRING_LANGUAGE_CODE", nullable = false)
    private String language;


....
}
```


Hier die Exception...


```
org.springframework.dao.DataIntegrityViolationException: StatementCallback; SQL [
INSERT INTO I18NSTRING (id, I18NSSTRING_LANGUAGE_CODE, I18NSSTRING_CONTENT) values (1, 'de', 'Messe Hannover (TEST)')]; integrity constraint violation: foreign key no parent; FK73DAB3AF4E0EC202 table: I18NSTRING; nested exception is java.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: foreign key no parent; FK73DAB3AF4E0EC202 table: I18NSTRING

....



	at Caused by: org.hsqldb.HsqlException: integrity constraint violation: foreign key no parent; FK73DAB3AF4E0EC202 table: I18NSTRING
	at org.hsqldb.error.Error.error(Unknown Source)
	at org.hsqldb.Constraint.getException(Unknown Source)
	at org.hsqldb.Constraint.checkInsert(Unknown Source)
	at org.hsqldb.StatementDML.performIntegrityChecks(Unknown Source)
	at org.hsqldb.StatementDML.insertSingleRow(Unknown Source)
	at org.hsqldb.StatementInsert.getResult(Unknown Source)
	at org.hsqldb.StatementDMQL.execute(Unknown Source)
	at org.hsqldb.Session.executeCompiledStatement(Unknown Source)
	at org.hsqldb.Session.executeDirectStatement(Unknown Source)
	at org.hsqldb.Session.execute(Unknown Source)
	... 37 more
```


----------



## AFlieger (17. Aug 2011)

> Kann ich nun in Tabellen, die über @ElementCollection angelegt wurden eigentlich vorab per SQL INSERTS füllen? (Ich weiß ja nicht wie genau Hibernate die Tabelle anlegt, welche Contraints beachtet werden müsse, etc.)



Ja, natürlich kannst du die manuell mit SQL's füllen.
Hibernate selbst legt meines Wissens keine Tabellen in der DB an.

Was du dabei halt beachten musst, ist, dass du die Parenttabelle vor der Childtabelle füllst, was du in deinem Fall nicht tust und dir eben eine Exception einfängst, weil du versuchst eine FOREIGN-KEY Spalte mit Daten zu füllen, zu der es noch keinen entsprechenden Eintrag in der Parenttabelle gibt.

Auf dein Beispiel bezogen versuchst du Einträge in die Tabelle I18NString  vorzunehmen ohne die Passenden Einträge in der Tabelle EVENT zu haben.


----------



## dmike (17. Aug 2011)

AFlieger hat gesagt.:


> Ja, natürlich kannst du die manuell mit SQL's füllen.
> Hibernate selbst legt meines Wissens keine Tabellen in der DB an.






AFlieger hat gesagt.:


> Was du dabei halt beachten musst, ist, dass du die Parenttabelle vor der Childtabelle füllst, was du in deinem Fall nicht tust und dir eben eine Exception einfängst, weil du versuchst eine FOREIGN-KEY Spalte mit Daten zu füllen, zu der es noch keinen entsprechenden Eintrag in der Parenttabelle gibt.
> 
> Auf dein Beispiel bezogen versuchst du Einträge in die Tabelle I18NString  vorzunehmen ohne die Passenden Einträge in der Tabelle EVENT zu haben.



Nur das INSERT EVENT steht im SQl Script doch eindeutig vor dem INSERT I18NString.  

Ich hab mir das DDL ausgeben lassen. Das Problem ist anscheinend, dass ich I18NStrings auch in einer anderen Klasse verwende, nämlich in der Klasse Salutation. Dort steht exakt das gleiche wie in der Klasse Event.


```
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Salutation extends AbstractPersistenceEntity {

    @Embedded
    private I18NStrings salutations = new I18NStrings ();
}
```


Im DDL sieht man wie dann auch wie der contraint auf die Salutation Tabelle erzeugt wird. 
Als wenn ich das richtig verstehe, dann können die beiden Klassen Event und Salutation keine Collection von I18NString Objekten als Value-Type gleichzeitig verwenden. Ich muss I18NString als Entity-Type rausfahren.  


```
create table I18NSTRING (id int8 not null, I18NSSTRING_LANGUAGE_CODE varchar(255) not null, I18NSSTRING_CONTENT varchar(255) not null, primary key (id, I18NSSTRING_LANGUAGE_CODE, I18NSSTRING_CONTENT));

create table Salutation (id int8 not null, creationTimestamp timestamp not null, logicallydeleted bool not null, modificationTimestamp timestamp not null, female bool not null unique, primary key (id));

create table Event (id int8 not null, creationTimestamp timestamp not null, logicallydeleted bool not null, modificationTimestamp timestamp not null, endDate timestamp, registrationEndDate timestamp, registrationStartDate timestamp, startDate timestamp, primary key (id));

alter table I18NSTRING add constraint FK73DAB3AF4E0EC202 foreign key (id) references Salutation;
```


----------



## AFlieger (17. Aug 2011)

> Als wenn ich das richtig verstehe, dann können die beiden Klassen Event und Salutation keine Collection von I18NString Objekten als Value-Type gleichzeitig verwenden. Ich muss I18NString als Entity-Type rausfahren.



Doch, das können sie schon, nur musst du darauf achten, dass eben alle FOREIGN-KEYS in I18NString auch  gefüllt sind.

In deinem Fall benötigst du halt zwingend Events und Salutations, bevor du einen I18NStrings einfügen kannst.


----------



## dmike (17. Aug 2011)

AFlieger hat gesagt.:


> Doch, das können sie schon, nur musst du darauf achten, dass eben alle FOREIGN-KEYS in I18NString auch  gefüllt sind.
> 
> In deinem Fall benötigst du halt zwingend Events und Salutations, bevor du einen I18NStrings einfügen kannst.




Ok, nur damit ich das richtig verstehe:


Der contraint in I18NString existiert ja nur für die Salutation Tabelle. Du hast nat. recht, wenn ich eine Salutation mit der ID 99 anlege und ein paar I18NStrings ebenfalls mit der ID 99 einfüge, dann geht das. Aber was fehlt ist dann die Tabelle Event. Sobald ich einen I18NString mit einer ID einfüge, die in der Tabelle Salutation nicht existiert, gehts in die Hose. Der Contraint auf Salutaion läßt das eben nicht zu. Deswegen denke ich dass das mit den Value-Types in dieser Konstellation nicht funzen kann.
Oder?


----------



## nillehammer (17. Aug 2011)

Deine Klasse I18NStrings ist ein Embeddable und soll ein Set von weiteren Embeddables (I18NString) enthalten. Das ist nicht möglich. Nur echte Entities können Embeddables enhalten. IMHO ist die Klasse I18NStrings auch überflüssig. Du könntest ein Set von I18NString direkt in Events referenzieren.


----------



## dmike (17. Aug 2011)

nillehammer hat gesagt.:


> Deine Klasse I18NStrings ist ein Embeddable und soll ein Set von weiteren Embeddables (I18NString) enthalten. Das ist nicht möglich. Nur echte Entities können Embeddables enhalten. IMHO ist die Klasse I18NStrings auch überflüssig. Du könntest ein Set von I18NString direkt in Events referenzieren.



Yep das macht Sinn! Danke!


----------

