Google App Engine DataStore com JDO e PHP

No artigo que explico a configuração inicial para rodar aplicações em PHP no Google App Engine (GAE), cito um pouco sobre a parte de persistência. Neste artigo pretendo aprofundar nesta parte, na prática.

Primeiro é importante lembrar que o GAE não oferece a API do JDBC, assim não podemos acessar bancos externos. Então no PHP não temos as funções comuns de BD, como mysql_connect ou PDO.

O banco de dados usado no GAE não segue um modelo relacional, como a maioria (principalmente no php) esta acostumado. GAE usa o datastore como um banco de dados baseado em Entidades. Essas entidades são como objetos, são na verdade estruturas de dados com chaves e valores.

Interessante notar que as estruturas são “schema-less”, ou seja, não precisam ser pré-definidas, os pares de chave=>valor serão gerados conforme forem enviados. Assim mudanças nos objetos não precisam serem “migradas” ao banco de dados.

Outro ponto importante nisto, é que, como não estamos lidando com dados relacionais, o pensamento de JOINs deve ser repensado. Na verdade, de preferencia, esquecidos. O datastore funciona muito bem em casos de relações proprietárias exclusivas, como classes “embedded”, mas JOIN como estamos acostumados em bancos relacionais são um inferno.

As entidades devem ser definidas como classes Java, usando as anotações devidas, e em seguida precisam ser “Enhanced” para gerarem o código da persistência em si. Vamos começar agora a gerar as nossas entidades.

O projeto será simples, como o : Teremos apenas User e Note. O pacote principal será o “app”, e o modelo estará em “app.model”. Então precisamos das pastas “src/app/model”. Faça o download do projeto e o coloque próximo ao SDK.

Vamos definir primeiro uma interface para identificar nossos objetos persistentes, essa será útil mais tarde. Em /src/app/model crie IModel.java .

Como disse, primeiro definimos nossas entidades, em Java. Primeiro a classe User, em src/app/model/User.java . Sem as partes das anotações, definimos a classe normalmente. Ele possui alguns dados básicos, e uma lista de Notas. Os atributos são privados e usamos acessores.

Agora vamos adcionar as anotações, e temos o User persistente. A primeira coisa é a anotação “PersistenceCapable” que define que a classe é persistente. Temos ainda a tag “PrimaryKey”, e a tag “Persistent”, que define que o atributo deve ser salvo. O “ValueStrategy” define que esse atributo será auto-gerado, então o próprio datastore vai gerar nossas chaves.

É importante ainda o “deatached=true” pois permite usar o objeto mesmo quando o datastore for fechado.

Repare porem que a lista de notas não é persistente, e sim “NotPersistent”, a tag é colocada para garantir que não vai haver conflito. Como disse antes vamos fazer as junções na mão.

Em seguida mapeamos a classes Note, sem a anotações. Temos alguns dados. Reparem no campo Text. Uso Text ao invés de String porque na datastore o string tem limite, então usamos o Text. Pode reparar ainda que temos o “User owner” que a parte de cá da relação, mas também temos o “Long userId” que é o que vai ser usado na realidade. Repare no método que define o “owner” automaticamente estou definindo o “userId”.

Fazemos então as anotações, dessa vez nada novo.

Agora que temos nossas classes mapeadas, podemos partir para parte de acesso ao banco de dados. Vou usar um modelo bem simples, além do PMF (padrão, já tem no blankphp) vou usar apenas uma classe auxiliar Storage (em /src/app) que vai me permitir acessar de forma geral o datastore. Agora vamos acompanhar a classe src.app.Storage método a método.

O primeiro método é o “getAll”, ele me permite recuperar todos os objetos de uma determinda classe, basta receber uma String que é o nome completo da classe ( “app.model.User” ou”app.model.Note”), e me retorna uma lista com elas.

Na primeira linha eu faço o acesso ao gerenciador de persistência, e digo que meus objetos não devem ficar alocados nele. Depois declaro a lista de model que vou retornar., crio a query, e a executo e armazeno o seu retorno em uma lista temporária.

Pegadinha número um. Para liberar os objetos vou passa-los a uma segunda lista.

Então, se houver objetos a retornar ( !=null e size >=1), eu crio minha lista, e a preencho com os objetos da temporária. Ainda garanto a associação com a persistência deles.

Caso qualquer coisa dê errado ou não tenha objetos, eu retorno nulo mesmo. E , lembre-se sempre, fecho o datastore.

O segundo , getWhere, funciona idêntico ao primeiro exceto que recebe uma clausula “where” e a adciona a query.

O terceiro, get, retorna um objeto pelo seu ID, sua chave primaria. Ele funciona de forma semelhante aos anteriores, exceto que na query eu identifico que quero pelo ID. E, ao invés de formar uma lista, eu recupero o primeiro item desta. Retorno este item (ou nulo) e, sempre, fecho o datastore.

O método sequinte, o quarto, é o put. Ele é o encarregado por salvar (tanto inserir, quanto atualizar) um objeto no datastore. Como pode ver a chamada é simples, temos apenas o “makePersistent”, que já usamos antes.

A lógica é simples, se eu passo um objeto ao makePersistent que não veio de uma consulta (que eu não recuperei com um dos métodos acima) será gerado um INSERT, caso seja um objeto já persistente, ou seja, que veio do datastore ele será atualizado.

O último método agora é o delete, sem mistérios.

Agora podemos compilar as classes com a seguinte linha, na raiz do projeto, use o comando:

“ant compile”

E “enhance” as classes com o comando:

“java -cp ../appengine-java-sdk/lib/appengine-tools-api.jar:war/WEB-INF/classes:../appengine-java-sdk/lib/user/appengine-api-1.0-sdk-1.2.1.jar com.google.appengine.tools.enhancer.Enhance war/WEB-INF/classes/app/model/User.class war/WEB-INF/classes/app/model/Note.class”

Enfim, a parte em Java esta pronta, no próximo vamos partir para a parte em PHP.

8 ideias sobre “Google App Engine DataStore com JDO e PHP

  1. Bem interessante, e complicado também,
    dá pra ver que seu nível de conhecimentos em OOP
    é bem avançado :p

    Vou estudar esse código :)

  2. Pois é Igor, nesse caso é preciso entender de OO mesmo, pq é bem chato o esquema.

    Básicamente o Storage.java não vai mudar muito, o que precisa adaptar para cada aplicação são as entidades, os model.

    Resumindo é uma classe do model para cada “tabela”, e um atributo para cada “coluna”.

  3. Eu baixei isso ae, dei uma olhada superficial no código,
    mas não cheguei a analisar a fundo. :P

    O código do WordPress por si só já é complicado, imagina
    modificado pra rodar no GAE. :O

    Acho que vou dar mais uma olhada.

    Abraços

  4. Estou tão acostumado com os bancos de dados relacionais que coisas tão simples parecem ser impossíveis com o datastore.
    Minha primeira dúvida é com relação à índices únicos.
    Eles existem no datastore? Como garantir que não existirão registros duplicados? E nos casos de chave composta? Como lidar com isso tudo?

    Você tem algum site interessante sobre isso?
    Um tutorial para auxiliar as pessoas que estão migrando de um banco de dados relacional para um datastore seria bem interessante. Não acha?

    Abraço!

Comentários encerrados.