# Hibernate und Vererbung



## Bergmann (23. Apr 2011)

Hey,

ich hab jetzt schon einige Topics zu Vererbung gelesen, aber für mein Problem noch keine Lösung gefunden. Ich habe folgende Klassen: 


```
@MappedSuperclass
public class Title implements Serializable{
	
    public static final int TYPE_GAME  = 0;
    public static final int TYPE_MOVIE = 1;
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long uid;
    @Version
    private int version;
    
    private String name, description;
    
    protected int type; //Typ des Titels - wird von Superklassen gesetzt
    
    private String name, description;
    
    protected int type; //Typ des Titels - wird von Superklassen gesetzt

    /* ... */
}

@Entity
public class Game extends Title implements Serializable{
	
    private String genre;
	
	public String getGenre(){
		return genre;
	}
	
	public void setGenre(String _genre){
		genre = _genre;
	}
	
    @Override
    public String toString() {
        return "GAME: " + super.toString() + ", " + genre;
    }
	
	public Game(){
		this("", "", "");
	}
	
	public Game(String _name, String _description, String _genre){
		super(_name, _description);
		genre = _genre;
		type  = TYPE_GAME;
	}
}

@Entity
public class Movie extends Title implements Serializable{

    private String genre;
	
	public String getGenre(){
		return genre;
	}
	
	public void setGenre(String _genre){
		genre = _genre;
	}	
	
    @Override
    public String toString() {
        return "MOVIE: " + super.toString() + ", " + genre;
    }	
	
	public Movie(){
		this("", "", "");
	}
	
	public Movie(String _name, String _description, String _genre){
		super(_name, _description);
		genre = _genre;
		type  = TYPE_MOVIE;
	}
}
```

Die Klassen Game und Movie sind zur Zeit noch fast identisch, aber das ändert sich später noch. Dann habe ich verschiedene Klassen, die Titel benutzten. Die Titel wurden aber nicht als Titel erzeugt, sondern als Game oder Movie und dann bloß an ein Feld vom Typ Titel übergeben. Dazu hat Titel auch das Feld 'type' das ich auf Game oder Movie casten kann, wenn ich es benötige. Soweit so gut. Jetzt will ich die Titel mit Hibernate in der Datenbank speichern. Aber Hibernate weiß nicht, das der Titel eigentlich ein Game bzw ein Movie ist. Casten geht ja an der Stelle auch nich wirklich. Wie bekomm ich nun die Daten in die Datenbank?

MfG & Thx Bergmann.


----------



## JimPanse (23. Apr 2011)

Ich hoffe ich habe es richtig verstanden, wenn ja:

Kann Hibernate Title nicht speichern weil es keine Entity ist! 

```
@MappedSuperclass:
```
Wenn du Attribute vererben willst die gleich sind!


```
@Entity
@Inheritance(strategy = InheritanceType.<Strategy>)
```
Wäre hier sinnvoller! Dann brauchst du noch eine geeignete Strategie (JOINED, SINGLE_TABLE oder TABLE_PER_CLASS).

In deinem Fall wäre JOINED am sinnvollsten, d.h. alle Attribute die gleich sind landen in der Tabelle Title -> die abgeleiten Klassen + Attributen landen in einer eigenen Tabelle.


----------



## Bergmann (24. Apr 2011)

Hey,

hast mich leider falsch verstanden, war auch bisl komisch geschrieben. Hier ma n kleines Bsp was ich vor habe:


```
//Klasse die einen Titel besitzt:
@Entity
public class Medium implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long uid;
    @Version
    private int version;

    /*...*/

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "titleID")
    private Title title; //Title, der auf dem Medium gespeichert ist

    /*...*/

    public Title getTitle(){ 
    	return title;
    }

    public void setTitle(Title _title){
    	title = _title;
    }
}

//ein Titel wird an die Klasse übergeben:
Game g = new Game();
medium.setTitle(g);

//arbeiten mit dem Title:
Titel t = medium.getTitle();
switch(t.getType()){
    case Title.TYPE_GAME:{
        Game g = t;
        //Code der mit Game arbeitet
    }break;
    case Titel.TYPE_MOVIE:{
        Movie m = t;
        //Code der mit Movie arbeitet
    }break;
    case Title.TYPE_TITLE:{
        throw new Exception("...");
    }
}
```
Es gibt also nie eine Instanz von Title, es werden immer nur Games und Movies erzeugt und Title wird dazu genutzt die Objekte zu speichern. Und jetzt will ich in Hibernate das Game bzw. das Movie speichern, nicht den Titel. Ich könnte natürlich auch für jede Kindklasse ein eigenes Feld in Medium anlegen, aber da das es noch ein paar mehr werden, wird das dann ziemlich unübersichtlich und die Vererbung ist ja auch dazu gedacht das so zu lösen wie ich das jetzt hab.

MfG Bergmann.


----------



## JimPanse (25. Apr 2011)

???? Ich verstehe nicht was du vor hast

"...Es gibt also nie eine Instanz von Title"!

```
@OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "titleID")
    private Title title; //Title, der auf dem Medium gespeichert ist
```

Bedeutet aber: Es gibt eine 1:1 von Title zu Medium! Also muss Title auch als solche in der DB vorhandenen sein, weil wie soll sonst die Referenzierung erfolgen? 


```
Game g = new Game();
medium.setTitle(g);
 
//arbeiten mit dem Title:
Titel t = medium.getTitle();
switch(t.getType()){
    case Title.TYPE_GAME:{
        Game g = t;
        //Code der mit Game arbeitet
    }break;
    case Titel.TYPE_MOVIE:{
        Movie m = t;
        //Code der mit Movie arbeitet
    }break;
    case Title.TYPE_TITLE:{
        throw new Exception("...");
    }
}
```

Wenn du so vorgehen willst, dann musst du die Vererbung nutzen so wie ich sie dir vorher beschrieben habe!


----------



## Bergmann (26. Apr 2011)

Hey,

wenn es eine 1:1 Beziehung wäre würde ich dir Recht geben, aber von Medium zu Titel gibt es eine n:1 Beziehung. Mehrere Medien (DVD, BlueRay, VHS, ...) können ein und den selben Titel haben.
Vlt ist es aber auch besser, da eine Verebung zu nutzen. Dann sind zwar einige Daten doppelt, aber es ist einfacher zu verwalten. Das muss ich aber erst mit meinen Teamkollegen besprechen...

MfG Bergmann.


----------



## JimPanse (26. Apr 2011)

Bergmann hat gesagt.:


> Hey,
> 
> wenn es eine 1:1 Beziehung wäre würde ich dir Recht geben, aber von Medium zu Titel gibt es eine n:1 Beziehung. Mehrere Medien (DVD, BlueRay, VHS, ...) können ein und den selben Titel haben.
> Vlt ist es aber auch besser, da eine Verebung zu nutzen. Dann sind zwar einige Daten doppelt, aber es ist einfacher zu verwalten. Das muss ich aber erst mit meinen Teamkollegen besprechen...
> ...



Du verwirrst mich  du hast aber eine 1:1 Beziehnung modelliert:

```
@OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "titleID")
    private Title title;
```


wenn dann:

```
public class Medium{
@ManyToOne
private Title title;
...
}
```

;-)


----------



## Bergmann (27. Apr 2011)

Hey,



JimPanse hat gesagt.:


> Du verwirrst mich


 Das liegt daran, das ich selber verwirrt war^^ Aber wir haben heut in der Vorlesung unseren Prof gefragt und jetz gehts:

```
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Title implements Serializable{
	
	public static final int TYPE_TITLE = -1;
	public static final int TYPE_GAME  =  0;
	public static final int TYPE_MOVIE =  1;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long uid;
    @Version
    private int version;
    
    private String name, description;
    
    protected int type; //Typ des Titels - wird von Superklassen gesetzt

    /*...*/
}

@Entity
public class Game extends Title implements Serializable{
	
	private String genre;
		
	public String getGenre(){
		return genre;
	}
	
	public void setGenre(String _genre){
		genre = _genre;
	}

    /*...*/
}

@Entity
public class Medium implements Serializable {

    /*...*/
    
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "titleID")
    private Title title; //Title, der auf dem Medium gespeichert ist

    /*...*/
}
```

Danke für die Hilfe und die Nerven 

MfG Bergmann


----------



## mvitz (27. Apr 2011)

Ich möchte dazu noch etwas anmerken:

Wenn ihr im Title über Integer Konstanten den Typ der späteren Subklasse euch merkt, ist das äußerst ungünstig, da die Vaterklasse ihre Kinder ja nicht kennen soll/braucht. Wenn ihr wirklich irgendwann von Title auf z.B. Game casten müsst, solltet ihr das an der Stelle mit "instance of" machen und nicht über eine Konstante der Vaterklasse. Und wenn ihr das trotzdem so lasst, würde ich das ganze wenigstens über ein ENUM machen und nicht über int.


----------



## nocturne (27. Apr 2011)

warum nehmt ihr nciht den enexus database designer?


----------



## Bergmann (29. Apr 2011)

Hey,

@mvitz: Danke, werd das noch ändern. Hab noch nich so viel mit Java gemacht 
@nocturne: Hibernate is ne Vorgabe vom Prof -.-

MfG Bergmann


----------



## nocturne (15. Mai 2011)

Bergmann hat gesagt.:


> Hey,
> 
> @mvitz: Danke, werd das noch ändern. Hab noch nich so viel mit Java gemacht
> @nocturne: Hibernate is ne Vorgabe vom Prof -.-
> ...



edbd erzeugt hibernate-klassen und datenbanken.



> Programmiersprachen
> 
> * J5EE Beans mit JavaDoc (Java 5 Enterprise Edition)
> Die Vererbung der Tabellen der PostgreSQL-Datenbank wird auf die Java-Beans übernommen.
> ...




Hier mein Beispiel:
CREATE TABLE "public"."name" (
  "label" TEXT
) WITHOUT OIDS;

CREATE TABLE "public"."mandant" (
  "mandantid" BIGSERIAL, 
  "contact" TEXT, 
  CONSTRAINT "mandant_pkey" PRIMARY KEY("mandantid")
) INHERITS ("public"."name")
WITHOUT OIDS;


Und Java:

```
package org.enexus.releasemanagement.entitybeans;
import java.io.Serializable;
import org.hibernate.validator.*;
import javax.validation.constraints.*;
import org.hibernate.envers.Audited;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import org.hibernate.annotations.AccessType;
import javax.faces.model.SelectItem;
import org.hibernate.validator.Pattern;
import org.hibernate.validator.NotNull;
/**
 * <hr/>
 * <b style=font-size:12pt>Name</b>
 * <ul>
 *   <li>{@link #getLabel Label} </li>
 * </ul>
 */
@Audited
@MappedSuperclass
@AccessType("property")
@SuppressWarnings({"unused", "serial"})
public class Name extends SelectItem implements Serializable{
  // class fields
 
  private String label;

  public Name(){
    super();
    setValue(this);
  }
 
 

  @Length(max=24,min=3)
  /**  
   */  
  @Column(name = "label")
  public String getLabel() {return this.label;};
  public void setLabel(String label) {
    this.label = label;
  };
}
```

und [JAVA=27]
package org.enexus.releasemanagement.entitybeans;
import java.io.Serializable;
import org.hibernate.validator.*;
import javax.validation.constraints.*;
import org.hibernate.envers.Audited;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import org.hibernate.annotations.AccessType;
import javax.faces.model.SelectItem;
import org.hibernate.validator.Pattern;
import org.hibernate.validator.NotNull;
/**
 * <hr/>
 * <b style=font-size:12pt>Der Mandant einer Firma</b>
 * <ul>
 *   <li>{@link #getMandantid Mandantid} </li>
 *   <li>{@link #getContact Contact} </li>
 * </ul>
 */
@Audited
@Entity
@Table(name= "mandant")
@AccessType("property")
@SuppressWarnings({"unused", "serial"})
public class Mandant extends Name implements Serializable{
  // class fields

  private int mandantid;

  private String contact;

  public Mandant(){
    super();
    setValue(this);
  }



  /**  
   */  
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="mandant_mandantid_seq")
  @SequenceGenerator(sequenceName="mandant_mandantid_seq", name="mandant_mandantid_seq", allocationSize=1)
  @Column(name = "mandantid", insertable = false)  // auto increment 
  public int getMandantid() {return this.mandantid;};
  public void setMandantid(int mandantid) {
    this.mandantid = mandantid;
  };
 // project_group
  /**
   * Many-To-One
   * 
   * @deprecated Please use {@link ProjectGroup#setMandant(ProjectGroup) ProjectGroup.setMandant()} instead 
   */
  @OneToMany(mappedBy = "mandant", fetch = FetchType.LAZY)
  public List<ProjectGroup> getProjektgruppen(){return this.projektgruppen;}
  List<ProjectGroup> projektgruppen = new ArrayList<ProjectGroup>();
  public void setProjektgruppen(List<ProjectGroup> projektgruppen){this.projektgruppen = projektgruppen;}



  @Length(max=24,min=3)
  /**  
   */  
  @Column(name = "contact")
  public String getContact() {return this.contact;};
  public void setContact(String contact) {
    this.contact = contact;
  };
}

[/code]


----------

