Let's design a simple blog system and persistence domain objects in both database and content repository.
The Class diagram for this simple blog system shows below:
In traditional database development:
1. Choose Database Management System, MySQL.
2. create schema "blog.sql" to define data structure:
CREATE DATABASE blog;
USE blog;
CREATE TABLE BLOGGER(
ID bigint(20) NOT NULL auto_increment,
NAME varchar(50) NOT NULL,
PASSWORD varchar(50) NOT NULL,
EMAIL varchar(80) NOT NULL,
PRIMARY KEY (ID)
) DEFAULT CHARSET=utf8;
CREATE TABLE POST(
ID bigint(20) NOT NULL auto_increment,
TITLE varchar(100) NOT NULL,
CONTENT text NOT NULL,
POSTTIME datetime default NULL,
BLOGGER_ID bigint(20) NOT NULL,
PRIMARY KEY (ID)
) DEFAULT CHARSET=utf8;
CREATE TABLE COMMENT(
ID bigint(20) NOT NULL auto_increment,
TITLE varchar(100) NOT NULL,
CONTENT text NOT NULL,
POSTTIME datetime default NULL,
POST_ID bigint(20) NOT NULL,
PRIMARY KEY (ID)
) DEFAULT CHARSET=utf8;
ALTER TABLE POST ADD CONSTRAINT FOREIGN KEY( BLOGGER_ID) references BLOGGER(ID);
ALTER TABLE COMMENT ADD CONSTRAINT FOREIGN KEY( POST_ID) references POST (ID);
3. do ORM mapping use JPA annotation
@Entity
@Table(name = "BLOGGER")
public class Blogger {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name = "EMAIL")
private String email;
@Column(name = "NAME")
private String name;
@Column(name = "PASSWORD")
private String password;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Listposts;
}
@Entity
@Table(name = "POST")
public class Post {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name = "TITLE")
private String title;
@Column(name = "CONTENT")
private String content;
@Column(name = "POSTTIME")
private Date postTime;
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private Blogger blogger;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Listcomments;
}
@Entity
@Table(name = "COMMENT")
public class Comment {
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name = "COMMENTER")
private String commenter;
@Column(name = "CONTENT")
private String content;
@Column(name = "POSTTIME")
private Date postTime;
}
Now, let's have a look how to do those in JCR Content Development
1. Choose content repository implementation, Apache Jackrabbit
2.In JCR world, we do "Content type definition". I will use xml since most of us are familiar with it. It also can be done in Compact Node type Definition(CND), but you have to learn the syntax.
xmlns:rep="internal" xmlns:sv="http://www.jcp.org/jcr/sv/1.0"
xmlns:test="http://www.apache.org/jackrabbit/test" xmlns:mix="http://www.jcp.org/jcr/mix/1.0"
xmlns:ocm="http://jackrabbit.apache.org/ocm" xmlns:ax="http://absorbx.com/jcr">
nt:base
requiredType="String" autoCreated="false" mandatory="true"
onParentVersion="COPY" protected="false" multiple="false" />
mix:referenceable
nt:unstructured
autoCreated="false" mandatory="true" onParentVersion="COPY"
protected="false" multiple="false" />
requiredType="String" autoCreated="false" mandatory="true"
onParentVersion="COPY" protected="false" multiple="false" />
autoCreated="false" mandatory="true" onParentVersion="COPY"
protected="false" multiple="false" />
autoCreated="false" mandatory="true" onParentVersion="COPY"
protected="false" multiple="true" />
mix:referenceable
nt:unstructured
autoCreated="false" mandatory="true" onParentVersion="COPY"
protected="false" multiple="false" />
autoCreated="false" mandatory="false" onParentVersion="COPY"
protected="false" multiple="false" />
requiredType="Date" autoCreated="false" mandatory="false"
onParentVersion="COPY" protected="false" multiple="false" />
autoCreated="false" mandatory="true" onParentVersion="COPY"
protected="false" multiple="false" />
requiredType="String" autoCreated="false" mandatory="true"
onParentVersion="COPY" protected="false" multiple="true" />
mix:referenceable
nt:unstructured
requiredType="Date" autoCreated="false" mandatory="false"
onParentVersion="COPY" protected="false" multiple="false" />
requiredType="String" autoCreated="false" mandatory="false"
onParentVersion="COPY" protected="false" multiple="false" />
autoCreated="false" mandatory="false" onParentVersion="COPY"
protected="false" multiple="false" />
3. We need to define path in order to implement similar foreign key, to define relationship between table. Node in JCR repository is tree structure, relationship can be done by prent->children.
in this case, we just use the simplest structure:
root-blog-bloger1-post1-comment1
|-comment2
|-post2-comment1
-|bloger2-post1-comment1
In this path structure, one bloger's posts can be get by retrieve its children. A post's prent is the author.
4. let's do Oject Content Mapping.
import java.util.List;
import org.apache.jackrabbit.ocm.manager.collectionconverter.impl.NTCollectionConverterImpl;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Collection;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Field;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Node;
@Node(jcrType = "ax:blogger")
public class Blogger {
@Field(uuid = true)
private String uuid;
@Field(jcrName = "ax:email")
private String email;
@Field(jcrName = "ax:name")
private String name;
@Field(jcrName = "ax:password")
private String password;
@Collection(collectionConverter=NTCollectionConverterImpl.class,jcrName="ax:posts",jcrType="ax:post")
private Listposts;
}
import java.util.Calendar;
import java.util.List;
import org.apache.jackrabbit.ocm.manager.beanconverter.impl.ParentBeanConverterImpl;
import org.apache.jackrabbit.ocm.manager.collectionconverter.impl.NTCollectionConverterImpl;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Bean;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Collection;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Field;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Node;
@Node(jcrType = "ax:post")
public class Post {
@Field(uuid = true)
private String uuid;
@Field(jcrName = "ax:title")
private String title;
@Field(jcrName = "ax:content")
private String content;
@Field(jcrName = "ax:postTime")
private Calendar postTime = Calendar.getInstance();
@Bean(jcrType = "ax:blogger", jcrOnParentVersion = "IGNORE", jcrName = "ax:blooger", converter = ParentBeanConverterImpl.class)
private Blogger blogger;
@Collection(collectionConverter=NTCollectionConverterImpl.class,jcrName="ax:comments",jcrType="ax:comment")
private Listcomments;
}
import java.util.Calendar;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Field;
import org.apache.jackrabbit.ocm.mapper.impl.annotation.Node;
@Node(jcrType = "ax:comment")
public class Comment {
@Field(uuid = true)
private String uuid;
@Field(jcrName = "ax:commenter")
private String commenter;
@Field(jcrName = "ax:content")
private String content;
@Field(jcrName = "ax:postTime")
private Calendar postTime = Calendar.getInstance();
}
