hi guys
alright we want to talk about a very useful API
this is JPA(Java Persistence API)
alright
you are a Java EE developer
so how do you connect to your Database (MySQL, Oracle,...) in Servlet or backbean?
for beginners like me, we make a connection and create a data source with web server or directly from our database engine
it works without any problem
for example in a simple Servlet
...
InitialContext conx = new InitialContext();
DataSource db=(DataSource) conx.lookup("jdbc/mysql");
Connection conn=db.getConnection();
PreparedStatement st=conn.prepareStatement("SELECT id,tvalue FROM test");
ResultSet res=st.executeQuery();
...
now forget it and let's start with JPA
lets learn it with an example, a simple library
at the first lets create database "myLib" (MySQL 5.1)
create database myLib \g
use myLib
then create 4 table "books", "comments", "authors" & a table for many-to-many relationship between "books" & "authors" call it "booksAuthors"
now create tables and relationships
books:
create table books(id int not null primary key auto_increment,name varchar(64),price real,) \g
comments:
create table comments(id int not null primary key auto_increment,name varchar(64) not null,email varchar(64) not null,text varchar(512) not null,forId int not null references books(id))
authors:
create table authors(id int not null primary key auto_increment,name varchar(64),email varchar(128))
booksAuthors:
create table booksAuthors(bookId int not null references books(id),authorId int not null references authors(id))
task:
after creating tables, insert records to DB
alright now lets create the entity classes
each entity class determine with @Entity annotation
before we start we should know about some General information
1.entity class should be Public
2.entity class attributes can not be public
3.for accessing to attributes should use of "setter" (setX()) and getter(getX()) methods
4.setter and getter methods can not be private
5.class should implements "Serializable" interface
this is will be our first entity class
for books
package _entityBeans;
import java.io.Serializable;
import javax.persistence.Entity; //[0]
import javax.persistence.Id;
import javax.persistence.Table;
@Entity //[1]
@Table(name="books") //[2]
public class books implements Serializable {
@Id //[3]
private int id; //[3]
private String name; //[4]
@Column(name="price")
private double cost; //[5]
public void setName(String name){this.name=name;} //[6]
public String getName(){return this.name;}
public void setId(int id){this.id=id;}
public int getId(){return this.id;}
public void setCost(double cost){this.cost=cost;}
public double getCost(){return this.cost;}
}
in above code
[0]: don't forget importing packages
[1]: it will tell web server that I'm an Entity bean
[2]: it specify the table that you want to create entity bean for
[3]: it shows table primary key column
[4]: in the database we have a column called "name"
[5]: we don't have any "cost" column in books table, so we use @Column to tell system "cost" attribute is equal with "price" in database
[6]: in any getter and setter method in java, we should use capital letter after set & get keyword
NOTE:don't forget, the entity's attributes data types should be equal with the tables column's data types in database
task:
make the above structure for authors & comments tables
NOTE: don't create attribute, getter, & setter for columns witch referenced from other table
for example
the "forId" column in "comments" table doesn't need any attribute, getter, and setter method in related entity class
now how does web server find specify database?
we will create a configuration file, that web server find specify database to work with
for example,
we have 3 different database system(MySQL, DB2, SQLite)
and in each DB we have 10 database, & every engines has Lib database
so now, we should tell web server witch engine we want to work with, and also about the database
for example MySQL, with Lib database
alright for this reason we should configure "persistence.xml" file
this file placed in "WEB-INF/classes/META-INF/" directory inside the WAR file
content of this file should be like this
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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">
<persistence-unit name="JPA" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/lib</jta-data-source>
<properties>
</properties>
</persistence-unit>
</persistence>
in above code we have <persistence-unit> element because we can more that one persistence unit in our application
the <jta-data-source> element point to JNDI name of our context in web server, it should be a data source
the <provider> tag means which provider we use for our persistence unit
I always use Hibernate JPA provider
some common providers are
org.hibernate.ejb.HibernatePersistence //the hibernate jpa provider
oracle.toplink.essentials.PersistenceProvider //toplink provider
org.apache.openjpa.persistence.PersistenceProviderImpl //apache openJPA provider
NOTE: if you omit <provider> tag, web server will use default provider, it usually is EclipseLink, don't forget add the related library!
NOTE: if we want use for example hibernate jpa provide or else, we should add the related library in our application
NOTE: if you omit <class> tag (a <persistence-unit> child), by default all of the entity classes accessible with this JPA unit, sometimes you want just add 3 of 10 entity classes in a JPA unit, for this reason you can use <class> tag
for example
<class>JBeans.books</class>
alright after that we should set relationship between entity beans
for example the "books" table has One-to-Many relation with "comments" table
is means one book can have more that one comment
OK after create both "books" and "comments" entity beans
now should specify relation between them
set the changes in "comments" entity bean as below
...
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
...
@ManyToOne //[0]
@JoinColumn(name="forId") //[1]
private books thisBook; //[2]
public void setThisBook(books thisBook){this.thisBook=thisBook;}
public books getThisBook(){return this.thisBook;}
...
in above code
[0]: @ManyToOne means that many comments for a books
[1]: "forId" is referenced column from books table(books.java)
NOTE: the following code is false and won't work
private int forId;
remember, in an entity bean, the referenced column types is a instance of target table(class)
don't create attribute for referenced columns
[2]: as we said it's an instance of books.class
now let's configure books.class too
as we know one book can many comments, it's mean for an instance of books.java may is more than one comments.java instance
look at code
...
import java.util.Set;
import javax.persistence.OneToMany;
...
@OneToMany(mappedBy="books") //[0]
private Set comments; //[1]
public void setComments(Set comments){this.comments=comments;}
public Set getComments(){return this.comments;}
...
in above code
[0]: @OneToMany means one books can have more than one comments
[0]: (mappedBy="X"), X is name of attribute in other class
[1]: create an instance of other class, it's should be a Set or Collection, because it may return more than one instance(sorry guys this section hard to explain)
NOTE: for one-to-one relation, use @OneToOne, don't mistake it never returns Set<> or Collection<> of class
alright, lets talk about books and authors relationships
so we have a many to many relationship here
one book can more than author, and one author can write more than one book
for this reason we have booksAuthors table
ok, let's config authors for many-to-many relation
//the authors.class
...
import javax.persistence.ManyToMany;
import javax.persistence.JoinTable;
import java.util.Set;
...
@ManyToMany
@JoinTable(name="booksAuthors",joinColumns=@JoinColumn(name="authorId",referencedColumnName="id"),inverseJoinColumns=@JoinColumn(name="bookId",referencedColumnName="id"))
//[0]
private Set books;
public void setBooks(Set books){this.book=book;}
public Set getBooks(){return this.books;}
...
in above code
@ManyToMany
it mean we have many-to-many relationship here
at @JoinColumns(name="A" referencedColumnName="X")
A:column in child
X:referenced column name in target table
and in books.class we will have
...
import java.util.Set;
import javax.persistence.ManyToMany;
...
@ManyToMany(mappedBy="books")
private Set authors;
public void setAuthors(Set authors){this.authors=authors;}
public Set getAuthors(){return this.authors;}
...
then after that lets create booksAuthors entity bean
so we have some difference
1.this table(in database engine) doesn't have any primary key
2.also system should check any user can not add duplicate rows
for example:
book1-author1
book1-author1 /// system should return error
we have booksAuthors.class
package _entityBeans;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="booksAuthors")
public class booksAuthors implements Serializable{
private int rootId;
private int autId;
public void setRootId(int rootId){this.rootId=rootId;}
public int getRootId(){return this.rootId;}
public void setAutId(int autId){this.autId=autId;}
public int getAutId(){return this.autId;}
}
in above code
as you see we don't have any primary key here (@Id)
it's good to make a composite primary key
Alright, for have composite PK in any JPA entity
we should make a separate class just for composite primary keys
so we will have "booksAuthorsPk.class" as below
package _entityBeans;
import java.io.Serializable;
public class booksAuthorsPk implements Serializable { //[0]
public int rootId; //[1]
public int autId;
public booksAuthorsPk() {} //[2]
public booksAuthorsPk(int rootId,int autId){this.rootId=rootId;this.autId=autId;}
@Override
public boolean equals(Object obj){ //[3]
boolean res=false;
if(obj==null){res=false;}
else if(!obj.getClass().equals(this.getClass())){res=false;}
else{_raRelPk oc =(_raRelPk)obj;
if(this==oc){res=true;}
else if(Integer.toString(this.rootId)!=null && Integer.toString(oc.rootId)!=null && Integer.toString(this.rootId).equals(Integer.toString(oc.rootId))){
if(Integer.toString(this.autId)!=null && Integer.toString(oc.autId)!=null && Integer.toString(this.autId).equals(Integer.toString(oc.autId))){
res=true;
}
}
}
return res;
}
@Override
public int hashCode() { //[3]
if(Integer.toString(this.rootId) == null || Integer.toString(this.autId) == null){
return 0;
}
else{return this.autId ^ this.rootId;}
}
}
well in above code
[0]: it should implements Serializable interface, class should be public
[1]: attributes should be public, or protected
[1]: attributes names should be same as (booksAuthors.class entity) those attributes
[2]: it should have a public constructor without any input arguments
[3]: equals(Object obj) & hashCode() methods should override
after that we will have some changes in our booksAuthors.class
package _entityBeans;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name="booksAuthors")
@IdClass(booksAuthorsPk.class)//[0]
public class booksAuthors implements Serializable{
@Id//[1]
private int rootId; //[2]
@Id
private int autId;
public void setRootId(int rootId){this.rootId=rootId;}
public int getRootId(){return this.rootId;}
public void setAutId(int autId){this.autId=autId;}
public int getAutId(){return this.autId;}
in above code
[0]: @IdClass annotation means we have a separate class for this entity's primary keys
[1]: well we have 2 Id (primary key) that shows this table has composite pk
[2]: again, attributes should be same, both in booksAuthors & booksAuthorsPk
Well, so what now?
alright we did just 50% of our job
now it's important how to use of these guys?
OK lets start with a simple servlet
for using JPA entities (in Servlet, backbeans,...) we should use @PersistenceUnit(unitName="JPA_unitName") annotation, EntityManagerFactory & EntityManager interfaces
check this servlet out
package backbeans;
import java.io.IOException;
import java.io.PrintWriter;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class books extends HttpServlet {
@PersistenceUnit(unitName="JPA") //[0]
private EntityManagerFactory EMF; //[1]
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
EntityManager EM=EMF.createEntityManager(); //[1]
int id=1;
Jbeans.books book;
String tid=request.getParameter("id");
if(!tid.equals(null)){id=Integer.parseInt(tid);}
out.println("<html>");
out.println("<head>");
out.println("<title>lets start with books</title>");
out.println("</head>");
out.println("<body>");
out.println("Servlet started");
test=EM.find(Jbeans.books.class, id); //[2]
out.print("<b>the wanted book id is</b>: "+id);
out.print("<b>book name </b> is: "+book.getTvalue());
out.print("<b>book price is :</b>"+book.getCost());
out.println("</body>");
out.println("</html>");EM.close(); //[7]
} catch(Exception err){out.print("An error occurred detail:<b>"+err.getMessage()+"</b>");}
finally {
out.close();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {processRequest(request, response);}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {processRequest(request, response);}
}
well in above code
[0]: @PersistenceUnit(unitName="x")
this guy tells to system, we want use of JPA, "x" is the your JPA unit name, that specified it in persistence.xml file
[1]: we should use EntityManagerFactory & EntityManager interface
[2]: I'll talk about this guy later
[7]: don't forget close your EntityManager instance, if you do, may web server throws nullPointException on deploying
the above code just get data from books table
now what we should to do for getting data with our persistence unit
as we see, we create an instance of EntityManager interface(EM)
remember, if we want catch record by "primary key" we can use find(class,Object) method
how does it work?
check this code out
...
int id=1; //for example
...
book = EM.find(JBeans.books.class,id)//[0]
...
in above code
[0]: classY y=EM.find(x.class,Object a)
x.class is name of your class, it should return classY's an instance
the a's data type should be same as x entity bean's primary key
for example:
class test has String based primary key
...
@Id
private String testPk;
...
and we want to get the record with "te007" as primary key value
...
String searchKey="te007";
...
test myTest=Em.find(test.class,searchKey);
...
don't forget find(x.class,Object a), the a's data type should be equal with x.class @Id's related fields
as we said each book can have comment(s)
let's improve our books Servlet, now we want get related book comment(s) if any
package backbeans;
import java.util.Set;
import java.io.IOException;
import java.io.PrintWriter;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class books extends HttpServlet {
@PersistenceUnit(unitName="JPA") //[0]
private EntityManagerFactory EMF; //[1]
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
EntityManager EM=EMF.createEntityManager(); //[1]
int id=1;
Jbeans.books book;
String tid=request.getParameter("id");
if(!tid.equals(null)){id=Integer.parseInt(tid);}
out.println("<html>");
out.println("<head>");
out.println("<title>lets start with books</title>");
out.println("</head>");
out.println("<body>");
out.println("Servlet started");
test=EM.find(Jbeans.books.class, id); //[2]
out.print("<b>the wanted book id is</b>: "+id);
out.print("<b>book name </b> is: "+book.getTvalue());
out.print("<b>book price is :</b>"+book.getCost());
out.print("<br/><br/><hr/><b>Comments</b>");
Set<comments> comments=book.getComments(); //[3]
out.print("<table>")
for(comments thisCom : comments){ //[4]
out.print("<tr><b>"+thisCom.getName()+"</b> said:</tr>");
out.print("<tr>"+thisCom.getText()+"</tr>");
out.print("<tr><hr/></tr>");
}
out.print("</table>")
out.println("</body>");
out.println("</html>");EM.close(); //[7]
} catch(Exception err){out.print("An error occurred detail:<b>"+err.getMessage()+"</b>");}
finally {
out.close();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {processRequest(request, response);}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {processRequest(request, response);}
}
</code>
well in above code:
[3]: create a Set<> of comments class, because it's one-to-many relation and may return more than one instance of comments class
[4]: in a for loop for each instance, getting data from comments entity bean
[7]: again, don't forget close this guy
as we saw, system get's related book comments
let's get book's related authors
it's same as getting comments
we will have this code
...
Set<authors> authors=book.getAuthors(); //[0]
out.print("<b>Authors</b>")
for(authors thisAuthor : authors){ //[2]
out.print("<a href=\"mailto:"+thisAuthor.getEmail()+"\">"+thisAuthor.getName()+"</a>");
}
out.print("</table>")
...
in above code
[0]: again, create a Set<> of authors class, because may one book written by 3 guy
[1]: again, in a for loop for each instance, getting data from authors entity bean
exercise:
create a Servlet get an author information and related books
Okay there is a little difference for getting data from booksAuthors table
as we know we can find a record with find(class<> class,Object a) method, where the a point to our primary key
so look at booksAuthors class, it has primary key, and separated class for keys, well for getting data from this guy we can use this code
...
JBeans.booksAuthors thisItem=EM.find(JBeans.booksAuthors.class,new booksAuthorsPk(1,3)); //[0]
...
in above code
[0]: our primary keys is a separate class
NOTE: if you remember we create a constructor in our booksAuthorsPk class for setting data easier, if you don't have this guy, you should create an instance of class and set each key(attribute) manually
we talked about getting data, so let's go for adding data
alright let's start with an example
for example we want leave a new comment for a book
check this code out
package backbeans;
import java.io.IOException;
import java.io.PrintWriter;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class leaveComment extends HttpServlet {
@PersistenceUnit(unitName="JPA") //[0]
private EntityManagerFactory EMF; //[1]
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
EntityManager EM=EMF.createEntityManager(); //[1]
int id=1;
Jbeans.books book;
Jbeans.comments comment=new Jbeans.comments();
String tid=request.getParameter("id");
if(!tid.equals(null)){id=Integer.parseInt(tid);}
String name=request.getParameter("name");
String email=request.getParameter("email");
String text=request.getParameter("text");
out.println("<html>");
out.println("<head>");
out.println("<title>leave new comment</title>");
out.println("</head>");
out.println("<body>");
out.println("Servlet started");
out.print("<b>your name:(will limit to 64 character)</b>: "+name);
out.print("<b>your email:(will limit to 64 character)</b> is: "+email);
out.print("<b>leave comment for book :</b>"+book.getName());
out.print("<br/><br/><hr/>Comments text(will limit to 512 character)<br/>"+text);
test=EM.find(Jbeans.books.class, id); //[2]
comment.setName(name.substring(0, 64)): //[3]
comment.setEmail(email.substring(0, 64));
comment.setText(text.substring(0, 512));
comment.setBook(book);
EM.getTransaction().begin(); //[4]
EM.persist(comment); //[5]
EM.getTransaction().commit()//[6]
out.println("</body>");
out.println("</html>");EM.close(); //[7]
} catch(Exception err){out.print("An error occurred detail:<b>"+err.getMessage()+"</b>");}
finally {
out.close();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {processRequest(request, response);}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {processRequest(request, response);}
}
in above code
[0]: I missed
[1]: whenever you want add a new record(s), create an instance of related class with itself constructor
[2]: find related book, which we want leave comment for
[3]: set information with setter methods, limit values to standard size, for example maximum size for name attribute in database is 64, then limit it to 64 character
[4]: for adding (every transactions) we should create an instance of EntityTransaction, so getTransaction() will return an instance of it
[4]: Start your transaction
[5]: the persist(Object a) method Makes an entity instance managed and persistent, it means get ready a object for transaction
[6]: commit and end the transaction,
NOTE: if you forget commit your transaction, no data will add in DB's and may web server throws exception
[7]: I said don't forget close this guy
well as this code, we added a new record in comments table
IMPORTANT NOTE:as you see we don't set id attribute value for adding new comment, why?
because we specified it as "auto_increment" when we were creating comments table, I mean system automatically set this attribute by database, so it's not necessary to set auto generation attributes for adding data
exercise:
create a Servlet that add a new record to books table
create a Servlet that add a new record to authors table
now
imagine an author (Mr. David R. Heffelfinger) wrote two books (Java EE 5 Development using GlassFish Application Server) and (Java EE 5 Development with NetBeans 6)
now we want store these information
alright check this code out
...
Set<JBeans.books> DavidBooks =(Set<JBeans.books>) new ArrayList(); //[0]
JBeans.books book1=new JBeans.books(); //[1]
book1.setName("Java EE 5 Development using GlassFish Application Server"); //[2]
book1.setCost(45);
JBeans.books book2=new JBeans.books();
book1.setName("Java EE 5 Development with NetBeans 6");
book1.setCost(45);
DavidBooks.add(book1);//[3]
DavidBooks.add(book2);
try{
EM.getTransaction().begin(); //[4]
EM.persist(book1); //[5]
EM.persist(book2);
JBeans.authors MrDavid=EM.find(JBeans.authors.class,7); //[6]
MrDavid.setBooks(DavidBooks);//[7]
EM.persist(DavidBooks); //[9]
EM.getTransaction().commit()//[10]
}catch(Exception err){out.print("An error occurred detail:<b>"+err.getMessage()+"</b>");}
...
well in above code
[0]: create a Set of books class
[1]: create a book instance
[2]: setting related information for book1 and book2 instances
[3]: add instances to the Set
[4]: begin the transaction
[5]: don't forget, at the first persist instances
[6]: It's necessary to know about Mr. Heffelfinger record's primary key, I mean know which primary key point to Mr. Heffelfinger
[7]: set the Set<> in authors instance,
[8]: again, I missed
[9]: now persist authors instance
[10]: commit the transaction
as you see it's easy
we talked about adding and getting data
now lets do some deleting!
now for deleting we will have this steps
1:find the related record (with find() method)
2:delete founded record (with remove() method)
it's really easy
for example we wanna delete a record from authors table, where primary key is 13
check this code out
...
try{
EM.getTransaction.begin();
JBeans.authors unluckyGuy = EM.find(JBeans.authors.class,13); //[0]
EM.remove(unluckyGuy); //[13]
EM.getTransaction().commit()
}catch(Exception err){out.print("An error occurred detail:<b>"+err.getMessage()+"</b>");}
...
in above code
[0]: find the Mr. unlucky
[13]: remove then Mr. unlucky
was it hard?
as you saw it's really easy for removing record with JPA
the remove() method works against persist() method
alright lets do something update
well it's as easy as ABC, even EFG
how did you add a new record?
create a instance of related class and begin the transaction
set the information
commit the transaction
updating a record is look like adding data
but for updating, create an instance with find() method but for adding create an instance from related class's constructor
for example we want update a comment's table record
...
EM.getTransaction.begin(); //[0]
JBeans.comments thisComm=EM.find(JBeans.comments.class,66); //[1]
thisComm.setEmail("we@love.java");[2]
EM.getTransaction.commit();//[3]
...
in above code
[0]: for updating, we need transaction, well get it and start it
[1]: find the related comment
[2]: set the new information with setter methods
[3]: commit the transaction
was it hard?
I'll talk about JPQL later, follow with my blog
and now:
for understanding JPA you should code it, debug it & develop it yourself,
Okay
do these tasks at home:
task #1: develop a joke system, that
1:user can join at system
2:each user can submit joke(s)
3:users can vote and leave comments on jokes
4:categorize jokes
task #2: develop a collage system, that
1:students can make group on system
2:students can join on groups
3:set many-to-many relationship between students and teachers
4:teachers can block student(s), deny access for blocked users
5:set many-to-many relationship between students and classes
6:set many-to-many relationship between teachers and teachers
if you have any problem(1:register at http://javaranch.com and ask your question, 2: ask me if I can will help you)
I hope this article were useful for you