Introduction à Google App Engine
L’été approche et chez Magetys ça bosse dur ! Nous sommes actuellement en train de développer deux applications Android qui vont arriver cet été et dans ce cadre, nous sommes confrontés à la problématique suivante : sachant que nos applications vont être des hits (haha), comment s’assurer d’une bonne qualité de service au fil du temps ?
C’est dans cette optique que nous nous sommes penchés sur Google App Engine. Google App Engine permet de développer et d’héberger des applications ‘On the Cloud’. Google vous propose ainsi d’heberger vos web apps (dans notre cas, nos back office) sur la même infrastructure que le fameux moteur de recherche. Ainsi, une des grande force d’App Engine est de permettre aux développeurs de ne pas se soucier de la partie configuration du serveur, sécurité, monté en charge etc… Google s’occupe de tout, et il faut avouer qu’en la matière ils en connaissent un rayon !
Actuellement, pour profiter d’App Engine on peut développer soit en Java soit en Python. Etant donné notre expertise (enorme!) en Java, notre choix s’est porté naturellement vers ce langage. Pour développer pour App Engine, Google fourni un plugin Eclipse très pratique. Ce plugin permet de créer et faire tourner son projet App Engine en local, visualiser le contenu de son datastore, simuler un système d’identification via des comptes Google et bien sûr, mettre en ligne son projet en un clic. Vous pouvez trouver le plugin ici.
Le datastore sous Google App Engine
Bien entendu, comme toute application, nous avons besoin de stocker des données. Comme beaucoup de développeurs ayant un background web, nous sommes habitués à MySQL et son driver JDBC. Sous App Engine, MySQL n’existe pas, adieu les bases de données relationnelles au profit du NoSQL ! NoSQL a été inventé pour développer des bases de données plus performantes en terme d’accès aux données. Google a développé BigTable, un système NoSQL que l’on peut exploiter sous App Engine. Pour des développeurs comme nous qui connaissons bien les bases de données relationnelles, il est un peu difficile de se mettre au NoSQL. Pourtant, ce n’est pas si compliqué que ça et l’on peut retrouver les mêmes mécanismes.
Dans ce tutorial, nous allons partir d’une modélisation relationnelle « à la MySQL » pour développer des objets Java qui pourront être enregistrés dans ce que l’on appelle le ‘datastore’ App Engine. Pour la couche de persistance, App Engine intègre les api JDO et JPA. Dans notre exemple nous aborderons JDO pour faire des requêtes sur le datastore.
Modélisation relationnelle
Nous allons partir d’un exemple simple. Dans le cadre d’un jeu, nous avons des utilisateurs, des niveaux et les utilisateurs obtiennent des scores pour les différents niveaux. Nous avons donc une relation 1,n / 1,n entre les utilisateurs et les niveaux que l’on peut représenter de cette manière dans un schéma MCD :
Si nous transformons notre schéma MCD en schéma MLD, nous obtenons ce schéma :
La table Score est née de la relation many-to-many entre User et Level. Si nous avions utilisé MySQL, nous aurions pu déduire de ce schéma, trois tables MySQL et des clés étrangères sur la table Score. Le code MySQL aurait été le suivant :
CREATE TABLE user ( user_id int NOT NULL AUTO_INCREMENT, login varchar(100), password varchar(100), PRIMARY KEY (user_id) ); CREATE TABLE level ( level_id int NOT NULL AUTO_INCREMENT, name varchar(100), PRIMARY KEY (level_id) ); CREATE TABLE score ( user_id int NOT NULL, level_id int NOT NULL, score int, PRIMARY KEY (user_id, level_id), FOREIGN KEY (user_id) REFERENCES user (user_id), FOREIGN KEY (level_id) REFERENCES level (level_id) );
Passage au Java
Sous App Engine, nous allons déduire de ce schéma, non pas trois tables, mais trois types d’objets Java qui pourront être sauvegardés dans le datastore. Sur les classes de ces objets, nous allons ajouter différentes annotations JDO pour indiquer quelles sont les données de ces classes qui seront enregistrées dans le datastore. Les annotations les plus importantes sont @PersistenceCapable pour indiquer qu’une classe peut être enregistrée dans le datastore et @Persistent pour indiquer qu’une donnée membre de la classe est à sauvegarder. Comme avec MySQL on peut utiliser des identifiants en AUTO_INCREMENT, avec App Engine, nous allons avoir un identifiant pour chaque objet sous forme d’un objet Key et nous allons laisser le soin à JDO de générer une clé unique pour chaque objet grâce à l’annotation @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Voici le code source des classes User et Level avec les annotations JDO pour qu’elles puissent être enregistrées dans le datastore :
@PersistenceCapable
public class Level {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String name;
public Level(String name) {
this.name = name;
}
public Key getKey() {
return key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "<level id=\"" + key + "\" name=\"" + name + "\" >";
}
}
@PersistenceCapable
public class User {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String login;
@Persistent
private String password;
public User(String login, String password) {
this.login = login;
this.password = password;
}
public Key getKey() {
return key;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "<user id=\"" + key + "\" " + "login=\"" + login + "\" "
+ "password=\"" + password + "\" >";
}
Passons maintenant à la classe Score qui va avoir, en plus de son identifiant, deux objets Key afin d’avoir une référence vers un User et un Level. On peut donc assimiler ces deux objets Key aux FOREIGN KEY bien connues en MySQL. Comme vous pouvez le remarquer on ne demande pas à JDO de générer d’identifiants uniques pour ces deux clés puisque nous allons utiliser les clés existantes de l’user et du level. Nous créons donc les setters (mutateur en francais) nécessaires.
@PersistenceCapable
public class Score {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private Key userKey;
@Persistent
private Key levelKey;
@Persistent
private long score;
public Score(Key userKey, Key levelKey, long score) {
this.userKey= userKey;
this.levelKey= levelKey;
this.score = score;
}
public Key getKey() {
return key;
}
public User getUser() {
return DBManager.getUser(user);
}
public void setUserKey(Key userKey) {
this.userKey= userKey;
}
public Level getLevel() {
return DBManager.getLevel(level);
}
public void setLevelKey(Key levelKey) {
this.setLevelKey= setLevelKey;
}
public void setScore(long score) {
this.score = score;
}
@Override
public String toString() {
return "< time = " + user + " " + " " + level + " <" + time + "> >";
}
}
Vous pouvez voir que dans cette classe, les getters (accesseurs en français) de User et Level passent par un DBManager. Nous vous parleront de cette classe dans la suite de ce tutorial où nous allons voir comment faire des requêtes sur le datastore avec JDO. J’espère que cette première partie du tutorial vous a plu et que vous allez vous aussi tenter de passer au ‘Cloud’ sur Google !


Ping : De MySQL à NoSQL avec Java Data Object (JDO) sous Google App Engine – Partie 2/2 | Magetys
Je ne comprends pas comment la ligne 28 fonctionne de la classe Score fonctionne:
« return DBManager.getUser(user); »
Ce ne serait pas plutôt « return DBManager.getUser(userKey); » ?
Bonjour,
Merci pour ce retour d’expérience, je trouve que pour une première approche cette exemple simple est vraiment bienvenu.
Je rejoins le commentaire de dmc, je pense que le paramètre de DBManager.getUser est plutôt l’id que l’objet de même que pour le getter de Level.
Encore merci.
À bientôt.