JPA GAE OneToOne

Dieses Thema JPA - GAE OneToOne im Forum "Data Tier" wurde erstellt von manuell, 3. Jan. 2013.

Thema: GAE OneToOne Hallo Forum, ich versuche nun seit geraumer Zeit eine einfache OneToOne-Beziehung ans laufen zu bekommen. Leider...

  1. Hallo Forum,

    ich versuche nun seit geraumer Zeit eine einfache OneToOne-Beziehung ans laufen zu bekommen.
    Leider entstehen immer diverse Fehler und ich komme auf keinen grünen Zweig.
    Die Herrausforderung ist folgende, ich möchte einen Benutzer anlegen, dann soll der Benutzer durch ein Event ein UserProfil bekommen (d.h. es existiert auch ein Benutzer ohne UserProfile).

    Aktuell habe ich folgendes:

    Code (Java):


    @Entity
    public class UserProfile {
        @OneToOne(mappedBy="userProfile")
        private User user;  
       
        private int id;
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }
    ...
     
    und User
    Code (Java):

    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;
        private String username;
        private String email;  
        @OneToOne
        private UserProfile userProfile;
       
       
        public UserProfile getUserProfile() {
            return userProfile;
        }
        public void setUserProfile(UserProfile userProfile) {
            this.userProfile = userProfile;
        }
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
    ...
     
    Beim Anlegen vom Benutzer passiert jetzt folgendes:
    Code (Java):

    INFO: Local Datastore initialized:
        Type: Master/Slave
        Storage: C:\Users\manuell\workspace\projekt\war\WEB-INF\appengine-generated\local_db.bin
    03.01.2013 07:01:16 com.google.appengine.api.datastore.dev.LocalDatastoreService load
    INFO: The backing store, C:\Users\manuell\workspace\projekt\war\WEB-INF\appengine-generated\local_db.bin, does not exist. It will be created.
    03.01.2013 07:01:17 org.datanucleus.state.JDOStateManager setPostStoreNewObjectId
    SCHWERWIEGEND: java.lang.NullPointerException
    03.01.2013 07:01:47 com.google.appengine.api.datastore.dev.LocalDatastoreService$PersistDatastore persist
    INFO: Time to persist datastore: 309 ms
     
    Ich habe als id auch schon Long / long als id benutzt, führte allerdings zu anderen Fehlern :)
    Wäre super wenn jemand ein funktionierendes Beispiel (für gae) hat.

    Danke für die Hilfe
     
  2. Vielleicht hilft dir das Java-Tutorial weiter. Hier klicken --> (Klick)
  3. Deine Beziehung ist bidirektional. Hast du beide Seite verbunden? Also so was wie

    User u = new User();
    UserProfile p = new UserProfile();
    u.setUserProfile(p);
    p.setUser(u);
     
  4. Hi,

    also den User kann ich problemlos anlegen und auch persistieren.
    Allerdings kann ich den Benutzer dann nicht auslesen
    so z.B.
    Code (Java):

    Query query = em.createQuery("SELECT u FROM User u WHERE u.email =:arg1");
            query.setParameter("arg1", email);
            User user;
            user = (User)query.getSingleResult();
     
    aber dann:
    Code (Java):

    java.lang.NullPointerException
        at com.google.appengine.datanucleus.query.QueryEntityPKFetchFieldManager.fetchLongField(QueryEntityPKFetchFieldManager.java:74)
        at org.datanucleus.identity.IdentityUtils.getApplicationIdentityForResultSetRow(IdentityUtils.java:101)
        at com.google.appengine.datanucleus.EntityUtils.entityToPojo(EntityUtils.java:1009)
     
    Bin mir nicht sicher an welcher Stelle ich das UserProfil setzen soll, brauche es ja erst später und nicht beim ersten persistieren.

    Danke für die Hilfe
     
  5. Hat das Entity für UserProfile auch eine OneToOne Annotation mit einem Userobjekt? Ich vermute, dass du automatisch eager loading hast und beim Auslesen des Users auf das UserProfile geladen werden soll, was nicht gesetzt ist und deshalb eine NPE wirft.

    Poste am besten mal die UserProfile Klasse. Als weitere Fehlersuche würde ich mal die OneToOne cascade auf lazy setzen bzw. einfach mal versuchen, ob ich nur den Benutzernamen per Query erhalten kann.

    Code (SQL):
    SELECT u.username FROM USER u WHERE u.email := email
     
  6. Also,
    ich habe die UserProfile jetzt mal etwas angepasst und sie sieht jetzt wie folgt aus:
    Code (Java):

    @Entity
    public class UserProfile {
        @OneToOne(mappedBy="userProfile")
        private User user;
       
        @Id
        private Key key;
       
        public Key getKey() {
            return key;
        }
        public void setKey(Key key) {
            this.key = key;
        }
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }
    }
     
    Ist jetzt wirklich nur für Testzwecke.
    Die User sieht so aus:
    Code (Java):

    @Entity
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private long id;
        private String username;
        private String email;  
        @OneToOne
        private UserProfile userProfile;
           
        public UserProfile getUserProfile() {
            return userProfile;
        }
        public void setUserProfile(UserProfile userProfile) {
            this.userProfile = userProfile;
        }
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        public long getId() {
            return id;
        }
        public void setId(long id) {
            this.id = id;
        }
            public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getEmail() {
            return email;
        }
        public void setEmail(String email) {
            this.email = email;
        }
    }
     
    auch erstmal aufs nötigste reduziert.
    Das ist meine EMF-Klasse:
    Code (Java):

    public class EMF {
        private static final EntityManagerFactory emfInstance = Persistence
                .createEntityManagerFactory("transactions-optional");
     
        private EMF() {
        }
     
        public static EntityManagerFactory get() {
            return emfInstance;
        }
    }
     
    so erzeuge ich den User:
    Code (Java):

    User newUser = new User();
    newUser.setUsername(Username);
    newUser.setEmail(Email);
    EntityManager em = EMF.get().createEntityManager();
    em.persist(newUser);
    em.close();
     
    Das klappt noch problemlos. (bis auf ein paar Warnungen)
    Sehe gerade das beim Abrufen des Benutzers folgende Warnung kommt:
    Code (Java):

    The datastore does not support joins and therefore cannot honor requests to place related objects in the default fetch group.  The field will be fetched lazily on first access.  You can modify this warning by setting the datanucleus.appengine.ignorableMetaDataBehavior property in your config.  A value of NONE will silence the warning.  A value of ERROR will turn the warning into an exception.
     
    beim nächsten Abrufen kommt dann der Fehler:
    Code (Java):

    java.lang.NullPointerException
        at com.google.appengine.datanucleus.query.QueryEntityPKFetchFieldManager.fetchLongField(QueryEntityPKFetchFieldManager.java:74)
        at org.datanucleus.identity.IdentityUtils.getApplicationIdentityForResultSetRow(IdentityUtils.java:101)
        at com.google.appengine.datanucleus.EntityUtils.entityToPojo(EntityUtils.java:1009)
     
    zur Sicherheit hier nochmal die persistence.xml
    Code (Java):

    <?xml version="1.0" encoding="UTF-8" ?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
            http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">

        <persistence-unit name="transactions-optional">
            <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
            <properties>
                <property name="datanucleus.NontransactionalRead" value="true"/>
                <property name="datanucleus.NontransactionalWrite" value="true"/>
                <property name="datanucleus.ConnectionURL" value="appengine"/>
            </properties>
        </persistence-unit>
    </persistence>
     
    ...Bin echt überfragt :)
     
  7. Die UserProfile Entity hat keine Id Generation Strategy weshalb die Id in der Datenbank nicht gesetzt wird und somit die Verbindung nicht funktionieren kann. Meistens hilft es auch mal die wirkliche generierten Datenbankwerte anschaut. Desweiteren solltest du dem Userobjekt auch ein UserProfile zuweisen.
     
  8. Schau dir jetzt hier den Kurs an und lernen Java zu programmieren: --> Hier klicken, um mehr zu erfahren (Klick)