# Primary Key Vergabe



## Oskar (25. Nov 2004)

Hallo ich schon wieder...

Wenn ich ein Entitybean definiere übernimmt dies die selbe Struktur wie auf der DB (was die Felder der Tabelle angeht). Auf der DB (mySQL) habe ich in der Tabelle ein Primarykey Feld vom Typ INTEGER. Dieses Feld wird im EJB durch ein Feld vom Typ java.lang.Integer dargestellt.

Mein Problem: 
Wie kann ich in der ejbCreate() Methode einen primary Key sauber erstellen? Gibt es hierfür eine Beispielhafte Implementierung? Wie lößt ihr die Vergabe von Primarykeys?  ???:L

Ich suche schon zum zweiten mal nach einer Lösung und habe bisher keine Idee einer Lösung geschweige denn einen Ansatz. 

Danke im Voraus
Oskar


----------



## Guest (26. Nov 2004)

Siehe Statement.executeUpdate(String sql, int *autoGeneratedKeys*), 
Statement.*RETURN_GENERATED_KEYS* 
und Statement.*getGeneratedKeys()*

Alternative: "Sequence-Pattern"


----------



## foobar (26. Nov 2004)

Um in einer CMP Entity Bean, unter Verwendung von Jboss, an den autoincrement Wert zu gelangen mußt du folgendes machen:
jboss-cmp-jdbc.xml:

```
<jbosscmp-jdbc>
   <defaults>
......	
     <entity-command name="mysql-get-generated-keys"/>
.......
   </defaults>
.....
<entity>
         <ejb-name>OrderBean</ejb-name>
         <table-name>orders</table-name>
    	<cmp-field>
       		<field-name>orderID</field-name>
			<column-name>ord_id</column-name>
	    </cmp-field>
	   <cmp-field>
			  <field-name>orderDate</field-name>
			  <column-name>ord_date</column-name>
	    </cmp-field>
		<cmp-field>
			  <field-name>customerName</field-name>
			  <column-name>ord_customer_name</column-name>
	    </cmp-field>
		<unknown-pk>
			<unknown-pk-class>java.lang.Integer</unknown-pk-class>
		   <field-name>orderID</field-name>
			<column-name>ord_id</column-name>
			<jdbc-type>INTEGER</jdbc-type>
			<sql-type>INTEGER</sql-type>
			<auto-increment/>
		</unknown-pk>
	</entity>		  
....
```

ejb-jar.xml

```
.......
<entity>
      <description></description>
      <ejb-name>OrderBean</ejb-name>
      <local-home>myshop.ejbs.entitie.OrderLocalHome</local-home>
      <local>myshop.ejbs.entitie.OrderLocal</local>
	  <ejb-class>myshop.ejbs.entitie.OrderBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <reentrant>True</reentrant>
    <cmp-version>2.x</cmp-version>
    <abstract-schema-name>OrderBean</abstract-schema-name>
    <cmp-field>
       <field-name>orderID</field-name>
    </cmp-field>
	   <cmp-field>
       <field-name>customerName</field-name>
    </cmp-field>
	<cmp-field>
       <field-name>orderDate</field-name>
    </cmp-field>
	<primkey-field>orderID</primkey-field>
.....
```


----------



## Guest (26. Nov 2004)

@foobar
Ich nahm an, er meint BMP, nicht CMP, da:


			
				Oskar hat gesagt.:
			
		

> ...Wie kann ich in der ejbCreate() Methode einen primary Key sauber erstellen?...


In CMP wird kein Primary-Key erstellt bzw. die ejbCreate-Methode (2.x Spezifikation) sollte null 
zurückgeben, daher ging ich davon aus, dass er BMP gemeint hat.
Nur mal so als Anmerkung...  :wink:


----------



## Oskar (26. Nov 2004)

...so gut.

Danke für die Hilfe. Werde das mit den Deployment descriptoren mal ausprobieren.

@Gast


> In CMP wird kein Primary-Key erstellt bzw. die ejbCreate-Methode (2.x Spezifikation) sollte null
> zurückgeben, daher ging ich davon aus, dass er BMP gemeint hat.



Sorry hatte ich vergessen. Ich meinte CMP.
Also dass ich in der ejbCreate() Methode keinen Primarykey zurückgebe ist mir bewußt. 
Nur wenn in der ejbCreate() Methode das Element erstellt und auf die DB geschrieben wird, dann müssen alle Felder gefüllt werden, die auf der DB als NOT NULL deklariert wurden (sonst gibt es scheene SQL Exceptions :x )
Daher muss ich doch auch hier ein evtl. PK Feld füllen. Es sei denn ich gehe davon aus, dass der PK beim Aufruf von ejbCreate() als Parameter übergeben wird, was ich persönlich irgendwie schlecht finde, denn was geht es den Nutzer des EJB an wie der PK gesetzt wird.

Naja noch mehr Verwirrung.

So far...
Oskar


----------



## foobar (26. Nov 2004)

> Es sei denn ich gehe davon aus, dass der PK beim Aufruf von ejbCreate() als Parameter übergeben wird, was ich persönlich irgendwie schlecht finde, denn was geht es den Nutzer des EJB an wie der PK gesetzt wird.


Aus diesem Grund gibt es das Element unknown-pk in der jboss-cmp-jdbc.xml, damit wird der Wert automatisch gesetzt.
Weitere Informationen findest du hier http://www.coredevelopers.net/library/jboss/cmp/keygen.jsp


----------



## Oskar (27. Nov 2004)

Ich muss nochmal was fragen...

Also wenn ich die Änderungen in den Meta Informationen wie beschrieben vornehme, legt er mir beim Ausführen der create() Methode ein neues Element auf der Datenbank an, und zählt auch schön brav den Auto-Increment Wert hoch.

Laut Doku Stellt JBoss über die Namensgleichheit zwischen dem unknown-pk field und einem existierenden cmp-field eine Inhaltliche Beziehung her. Also gehe ich davon aus, dass nach dem erstellen der Daten auf der DB auch die Inhalte von der DB in dem definierten Feld sind.

Nun ist es so, dass er den Daten satz zwar auf der DB anlegt, jedoch das Primary-Key Feld des EJBs nicht füllt, und mir dann eine Exception wirft, die besagt 


```
Primary key for created instance is null
```

Kann es daran liegen, dass ich einmal ein unknown-pk field definiere und andererseits eine pk-field für mein EJB anlege? (auf welches das unknown-pk field referenziert)

Wie sollte denn der Inhalt der auf der DB erstellt wird in das PK Field des EJBs kommen?

Danke im Voraus
Oskar


----------



## foobar (27. Nov 2004)

> Wie sollte denn der Inhalt der auf der DB erstellt wird in das PK Field des EJBs kommen?


In der jbosscmp-jdbc.xml werden doch alle benötigten Informationen angegeben (field_name, column_name etc.).
Wie sieht denn deine EntityBean aus? Welche Exception wird wann geworfen?


----------



## Oskar (27. Nov 2004)

Also...

meine jbosscmp-jdbc.xml sieht wie folgt aus:


```
<jbosscmp-jdbc>
   <defaults>
     <datasource>java:ForumDS</datasource>
     <datasource-mapping>mySQL</datasource-mapping>
     <entity-command name="mysql-get-generated-keys" />
   </defaults>

   <enterprise-beans>

        <entity>
         <ejb-name>User</ejb-name>
         <create-table>no</create-table>
         <remove-table>no</remove-table>
		
         <table-name>forum_user</table-name>

         <cmp-field>
            <field-name>userId</field-name>
            <column-name>user_id</column-name>
            <jdbc-type>INTEGER</jdbc-type>
            <sql-type>INTEGER</sql-type>
        </cmp-field>
         <cmp-field>
            <field-name>nickName</field-name>
            <column-name>nick_name</column-name>
            <jdbc-type>VARCHAR</jdbc-type>
            <sql-type>VARCHAR</sql-type>
        </cmp-field>
         <cmp-field>
            <field-name>EMail</field-name>
            <column-name>e_mail</column-name>
            <jdbc-type>VARCHAR</jdbc-type>
            <sql-type>VARCHAR</sql-type>
        </cmp-field>
		<unknown-pk>
	         <unknown-pk-class>java.lang.Integer</unknown-pk-class>
	         <field-name>userId</field-name>
	         <column-name>user_id</column-name>
	         <jdbc-type>INTEGER</jdbc-type>
	         <sql-type>INTEGER</sql-type>
	         <auto-increment />
      </unknown-pk>
      </entity>
   </enterprise-beans>
</jbosscmp-jdbc>
```

Das Feld, das als PrimKey verwendet werden soll ist userId.

Die ejb-jar.xml sieht do aus:



```
<ejb-jar >

   <description><![CDATA[No Description.]]></description>
   <display-name>Generated by XDoclet</display-name>

   <enterprise-beans>

     <entity >
         <description>Forum User</description>

         <ejb-name>User</ejb-name>

         <home>user.UserHome</home>
         <remote>user.User</remote>
         <local-home>user.UserLocalHome</local-home>
         <local>user.UserLocal</local>

         <ejb-class>user.UserCMP</ejb-class>
         <persistence-type>Container</persistence-type>
         <prim-key-class>java.lang.Integer</prim-key-class>
         <reentrant>True</reentrant>
         <cmp-version>2.x</cmp-version>
         <abstract-schema-name>forumUser</abstract-schema-name>
         <cmp-field >
            <description>User Id</description>
            <field-name>userId</field-name>
         </cmp-field>
         <cmp-field >
            <description>Nick Name</description>
            <field-name>nickName</field-name>
         </cmp-field>
         <cmp-field >
            <description>E-Mail Adress</description>
            <field-name>EMail</field-name>
         </cmp-field>
          <primkey-field>userId</primkey-field>

         <query>
            <query-method>
               <method-name>findAll</method-name>
               <method-params>
               </method-params>
            </query-method>
            <ejb-ql><![CDATA[SELECT OBJECT(a) FROM forum as a]]></ejb-ql>
         </query>
      </entity>

   </enterprise-beans>
   <assembly-descriptor >
   </assembly-descriptor>

</ejb-jar>
```

Hier ist userId als Prim key definiert.

Die Exception wird geworfen wenn ich in einem Servlet über home.create() eine Instanz des EJBs erzeuge. Sie lautet


```
StandardWrapperValve[CreateUser]: Servlet.service() for servlet CreateUser threw exception
javax.servlet.ServletException: Could not create User! Primary key for created instance is null.
```


Wie gesagt ein Blick auf die MySQL DB zeigt, dass die Daten erstellt werden.
Ich hoffe jetzt ist die Situation klar.

Oskar


----------



## foobar (27. Nov 2004)

Also die Deploymentdescriptoren sehen in Ordnung aus. Wie sieht denn der Code deiner EntityBean aus? Was machst du in der Create-Methode?


----------



## Oskar (28. Nov 2004)

Also ich vermute jetzt wirds peinlich...    

Bisher mache ich in der ejbCreate() Methode noch gar nix. Sollte ich? Muss ich da irgeneine Funktion auf der DB aufrufen?

Ich dachte das übernimmt die EJB Implementierung für mich wenn die Descriptoren stimmen.

Hm vielleicht ist es dann ja ganz einfach...

Oskar


----------



## foobar (28. Nov 2004)

Du mußt zumindest die übergebenen Werte an die entsprechenden cmp-Felder weiterleiten z.b. so:

```
public Integer ejbCreate(String name, String password) throws CreateException
{
    this.setName( name );
    this.setPassword( password );
    return null;
}
```


----------



## Bleiglanz (29. Nov 2004)

ahhhh,

wenn du deine PKs vom Container verwalten lassen willst, dann MUSS der Primary Key vom Typ Object sein, lies mal die Spec

also:

        <prim-key-class>java.lang.Object</prim-key-class>

Nebenbei: mit eine der schlimmsten Auslassungen in der EJB-Spec überhaupt, es gibt da allerhand Literatur dazu, wie man so einen Key-Generator von Hand nachimplementiert - ist aber alles furchtbarer Zeugs....

trotzdem nehme ich nie die SERIAL oder AUTOINCREMENT oder sonstwas der Datenbank her, weil es mir lieber ist, wenn innerhalb der Anwendung der PK vom richtigen Typ ist


----------



## foobar (29. Nov 2004)

@bleiglanz Wie lässt du deine PK's denn verwalten? Machst du das zu Fuß?


----------



## Bleiglanz (29. Nov 2004)

naja, da gibts ja viele schöne "ideen" dazu
mal ein high-low-schema (wie z.B. bei hibernate) usw. usf.

leider habe ich noch keine wirklich perfekte lösung gefunden, die

- im ejb-umfeld arbeitet
- transaktionssicher ist
- keine nebenläufigkeitsprobleme aufwirf
- nicht unbedingt den allerhöchsten Isolationslevel erfordert
- einigermassen einfach (programmatisch)  zu bedienenist

eigentlich mach ich jedes mal was anderes 

zuletzt hab ich mir eine dummy-Tabelle genommen mit einer SEQUENCE, aus der ich dann mit SELECT nextkey per direktem SQL-Zugriff immer neue nummern generieren lasse (und zwar ein globaler Zähler für ALLE Entities)

bei einer DuplicateKeyEx nehm ich einfach den nächsten...

ist nicht perfekt, aber irgendwie "Offline" ein "Pseudo-Singleton" zu implementieren ist mir wirklich zu aufwendig

Das bringt mich doch gleich zu einer weiteren schlimmen Auslassung in der EJB-Spec: kein ApplicationScope - während man für Webanwendungen wenigstens noch sowas wie den ServletContext hat, ist für ejb.jars keinerlei "globaler" Context vorgesehen, ahhhhhh


----------



## Guest (1. Dez 2004)

Ich sag' nur
<ejb-ref>Fluchen</ejb-ref>


----------



## Oskar (12. Dez 2004)

Hallo zusammen,

nach fast endlosen Kämpfen mit der IDE und den Descriptoren hier die Lösung wie es klappt. Vielleicht interessiert es ja noch jemanden.

ejb-jar.xml

```
<ejb-jar >
   <enterprise-beans>

      <entity >
         <display-name>UserBean</display-name>
         <ejb-name>User</ejb-name>

         <home>user.UserHome</home>
         <remote>user.User</remote>

         <ejb-class>user.UserCMP</ejb-class>
         <persistence-type>Container</persistence-type>
         <prim-key-class>java.lang.Object</prim-key-class>
         <reentrant>false</reentrant>
         <cmp-version>2.x</cmp-version>
         <cmp-field >
            <field-name>userId</field-name>
         </cmp-field>
         <cmp-field >
            <field-name>EMail</field-name>
         </cmp-field>
         <cmp-field >
            <field-name>nickName</field-name>
         </cmp-field>
         <cmp-field >
            <field-name>password</field-name>
         </cmp-field>

      </entity>
   </enterprise-beans>

   <assembly-descriptor >
   </assembly-descriptor>

</ejb-jar>
```

jbosscmp-jdbc.xml


```
<jbosscmp-jdbc>
   <defaults>
     <datasource>java:ForumDS</datasource>
     <datasource-mapping>mySQL</datasource-mapping>
     <create-table>false</create-table>
     <remove-table>false</remove-table>
   </defaults>

   <enterprise-beans>
      <entity>
         <ejb-name>User</ejb-name>

         <table-name>forum_user</table-name>

         <cmp-field>
            <field-name>userId</field-name>
			<read-only>true</read-only>
            <column-name>user_id</column-name>
            <jdbc-type>INTEGER</jdbc-type>
            <sql-type>INTEGER</sql-type>
	    <auto-increment/>
        </cmp-field>
         <cmp-field>
            <field-name>EMail</field-name>
            <column-name>e_mail</column-name>
            <jdbc-type>VARCHAR</jdbc-type>
            <sql-type>VARCHAR(45)</sql-type>
        </cmp-field>
         <cmp-field>
            <field-name>nickName</field-name>
            <column-name>nick_name</column-name>
            <jdbc-type>VARCHAR</jdbc-type>
            <sql-type>VARCHAR(45)</sql-type>
        </cmp-field>
         <cmp-field>
            <field-name>password</field-name>
            <column-name>password</column-name>
            <jdbc-type>VARCHAR</jdbc-type>
            <sql-type>VARCHAR(12)</sql-type>
        </cmp-field>

	<unknown-pk>
		<unknown-pk-class>java.lang.Object</unknown-pk-class>
		<field-name>userId</field-name>
		<column-name>user_id</column-name>
		<jdbc-type>INTEGER</jdbc-type>
		<sql-type>INTEGER</sql-type>
		<auto-increment/>
	</unknown-pk>

	<entity-command name="mysql-get-generated-keys">
	</entity-command>
      </entity>

   </enterprise-beans>

</jbosscmp-jdbc>
```

Danke für dir Tipps.

So far says
Oskar


----------

