In this blog i will be creating an EJB3 java application. This will not be just Hello World application as using enterprise bean for just printing a hello message will not be of much significance. I will create an application which will insert records into and read records from the database. I will be using the same database and tables that I used in previous blog "Spring-Hibernate integration". I will also demonstrate how to use JTA for Bean Managed Transactions(BMT). I will use JDBC for querying the databse but this time i will use a datasource to fetch pooled connection. I have already created a datasource in my Weblogic application server with the JNDI name jdbc/oracleDS. To demonstrate JTA I will use UserTransaction interface which I will inject in my stateless bean using dependency injection with the help of @Resource annotation.

Creating EJB application with EJB3 is as simple as creating a simple POJO. We just need to annotate the POJO class with @Stateless annotaiton because we are going to use Stateless bean in our application. So, lets get started and write our first stateless enterprise java beans.Lets name the project EJBDemoProject. As I will be deploying the applicaton on the Weblogic applicaiton server these are the JARS that will be required. And my folder structure will look like this.
Inside ejb package I will have my remote interface and the bean class. Inside the model package will be my Users model class.Inside utility i will have few utility classes like ConnecitonHelper,JDBCHelper and JNDIHelper which I will use to get connection, to get context for doing JNDI lookup and other JDBC related stuff. My stateless bean will be having two methods called insert() and getUsers() to insert and load data from the table using Users model class. For my user id column which is my primary key i am using oracle sequence to generate the sequence for me which i will use as primary key while inserting the record in the database. In case of hibernate it was all done by hibernate internally but as i am not using any ORM tool in this demo application I will have to do it manually through code. You will be able to understand when you see the code of each of them one by one. Lets first write our JNDIHelper class which will return the initial context to do a jndi lookup. Below is the complete code for this class-
package com.sanjiv.utility;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JNDIHelper {
public static Context getInitialContext(){
Context ctx = null;
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
prop.put(Context.PROVIDER_URL,"t3://localhost:7002,localhost:7003");
try {
ctx = new InitialContext(prop);
} catch (NamingException e) {
e.printStackTrace();
}
return ctx;
}
}
Dont get confused about my provider URL value as I am passing two values into it because my datasource is targeted to a cluster which has two managed servers. You can use as per the settings of your datasource. After that lets write the ConnectionHelper class which will return a connection object. This connection object will be fetched from the connection pool of the datasource. This is a simple class which takes the help of JNDIHelper class to get the init context and does the lookup of the datasource "jdbc/oracleDS", fetches the connection object from the datasource and returns the connection object. Below is the complete code for this-
package com.sanjiv.utility;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class ConnectionHelper {
private static final String DATASOURCE_NAME="jdbc/oracleDS";
static Connection con = null;
public static Connection getConnection(){
DataSource dataSource=null;
Context ctx = JNDIHelper.getInitialContext();
try {
dataSource = (DataSource) ctx.lookup(DATASOURCE_NAME);
con = dataSource.getConnection();
} catch (NamingException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
}
The JDBCHelper class have one method called getOracleSequnce() which I will use to fetch the primary key while inserting the record in the table. The JDBC code and UserTransaction methods like begin,commit and rollback throws a lot of exceptions. So to stop the bean code from getting cluttered with exception handling code I have declared these methods in this class like beginUserTransaction(), commitUserTransaction() and rollBackUserTransaction(), thise methods are quite trivial as i just invoke the UserTransaction methods from within these methods and do the exception handling here. This class has another utility method called closeResources() which will be used to close Connection, ResultSet and Statement objects. Below is the complete code for this class-
package com.sanjiv.utility;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
public class JDBCHelper {
public static long getOracleSequence(){
Connection con = ConnectionHelper.getConnection();
PreparedStatement pStmt = null;
ResultSet rs = null;
long oracleSequence = 0;
String query = "select userid_sequence.NEXTVAL from dual";
try {
pStmt = con.prepareStatement(query);
rs = pStmt.executeQuery(query);
while(rs.next()){
oracleSequence = rs.getLong(1);
}
} catch (SQLException e) {
e.printStackTrace();
}
finally{
closeResources(pStmt, rs, con);
}
return oracleSequence;
}
public static void closeResources(PreparedStatement pStmt, ResultSet rs, Connection con){
if(pStmt != null)
try {
pStmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(rs != null)
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(con != null)
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void beginUserTransaction(UserTransaction userTransaction){
try {
userTransaction.begin();
} catch (NotSupportedException e) {
e.printStackTrace();
} catch (SystemException e) {
e.printStackTrace();
}
}
public static void commitUserTransaction(UserTransaction userTransaction){
try {
userTransaction.commit();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RollbackException e) {
e.printStackTrace();
} catch (HeuristicMixedException e) {
e.printStackTrace();
} catch (HeuristicRollbackException e) {
e.printStackTrace();
} catch (SystemException e) {
e.printStackTrace();
}
}
public static void rollBackUserTransaction(UserTransaction userTransaction){
try {
userTransaction.rollback();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (SystemException e) {
e.printStackTrace();
}
}
}
You can clearly see how this class is cluttered with try-catch blocks.
Lets now write our first stateless enterprise java beans. When you create a new stateless bean in Eclipse it will create remote interface and corresponding bean classes for you. I have created a remote interface although I could have done with local interface as well as my EJB and client will be residing in the same JVM. The name of the remote interface will be the name of your bean + remote suffix attached to it. Its just the eclipse convention. You can give any name of your choice. The clients of EJB dont invoke the methods directly from the bean class rather they call the methods via interface. I have named my bean UsersBean and the interface UsersBeanInterface. The remote interface is always annotated with @Remote annotation. The bean interface will have just two methods. Below is the complete code for by interface.
package com.sanjiv.ejb;
import java.util.List;
import javax.ejb.Remote;
import com.sanjiv.model.Users;
@Remote
public interface UsersBeanRemote {
public void insert(Users user);
public List<Users> getUsers();
}
The bean class will be annotated with @Stateless annotation as its a stateless bean with mappedName="usersBean" which will the JNDI binding name for this bean and will be used to lookup the bean. Also I will be using bean managed transaction so I have another annotation @TransactionManagement(TransactionManagementType.BEAN),which tells the container that the transaction will be managed by the bean itself and not container. I am also using @Resource annotation to inject the UserTransaction in the bean as i will be using UserTransaction to manage the transaction inside the bean and will not be using JDBC transaction to handle the transaction. Although in this simple scenario JDBC would have sufficed as we are not doing any distributed transaction but just for demonstration and learning purpose i am using this to show how to handle transactions using JTA. Below is the complete code for the bean class which is quite easy to understand. There is nothing significant to explain here-
package com.sanjiv.ejb;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.transaction.UserTransaction;
import com.sanjiv.model.Users;
import com.sanjiv.utility.ConnectionHelper;
import com.sanjiv.utility.JDBCHelper;
/**
* Session Bean implementation class UsersBean
*/
@Stateless(mappedName = "usersBean")
@TransactionManagement(TransactionManagementType.BEAN)
public class UsersBean implements UsersBeanRemote {
@Resource
UserTransaction userTransaction;
public UsersBean() {}
@Override
public void insert(Users user) {
String query = "insert into users values(?,?,?)";
JDBCHelper.beginUserTransaction(userTransaction);
Connection con = ConnectionHelper.getConnection();
PreparedStatement ps = null;
try {
ps = con.prepareStatement(query);
ps.setLong(1, JDBCHelper.getOracleSequence());
ps.setString(2, user.getUserName());
ps.setString(3, user.getEmailId());
int count = ps.executeUpdate();
if(count > 0)System.out.println("Record Inserted");
else System.out.println("Failed to Insert Record");
} catch (SQLException e) {
//excetption occured roll back the transaction
JDBCHelper.rollBackUserTransaction(userTransaction);
e.printStackTrace();
}finally{
//commit the transaction
JDBCHelper.commitUserTransaction(userTransaction);
JDBCHelper.closeResources(ps, null, con);
}
}
@Override
public List<Users> getUsers() {
List<Users> userList = new ArrayList<Users>();
JDBCHelper.beginUserTransaction(userTransaction);
Connection con = ConnectionHelper.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
String query = "select * from Users";
ps = con.prepareStatement(query);
rs = ps.executeQuery();
populateUsers(rs, userList);
} catch (SQLException e) {
JDBCHelper.rollBackUserTransaction(userTransaction);
e.printStackTrace();
}
finally{
JDBCHelper.commitUserTransaction(userTransaction);
JDBCHelper.closeResources(ps, rs, con);
}
return userList;
}
private void populateUsers(ResultSet rs, List<Users>userList) {
try {
while(rs.next()){
Users user = new Users();
user.setId(rs.getLong("USER_ID"));
user.setUserName(rs.getString("USER_NAME"));
user.setEmailId(rs.getString("EMAIL_ID"));
userList.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
We have now created our first EJB3 application. Its time now to deploy the applicaiton on the target server which in my case is weblogic. Right click on the project->Export->Jar and save the jar file anywhere on the disk. After that login to you weblogic console, go to deployments, click on install, browse to the location where you saved the jar file and keep clicking on next till you reach the target server. Here select the target server where you want to deploy thie application. In my case i have select the AdminServer. Thats it we have now deployed our application on the server. We now need a java client to test our appliation. So, create another project in Eclipse, name it EJBDemoProjectClient or whatever you want. Add the EJB project jar in the classpath of this client. We also need weblogic.jar in the client project. Write a test client. In my case its TestDemoEJBProject and below is the complete code for the test client. This will both insert the record in the table and load the records from the table using the UsersBean insert() and getUsers() method-
package com.sanjiv.ejb.client;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import com.sanjiv.ejb.UsersBeanRemote;
import com.sanjiv.model.Users;
public class TestDemoEJBProject {
/**
* @param args
* @throws NamingException
*/
public static void main(String[] args) throws NamingException {
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
prop.put(Context.PROVIDER_URL,"t3://localhost:7001");
Context ctx = new InitialContext(prop);
UsersBeanRemote usersBean = (UsersBeanRemote)ctx.lookup("usersBean#com.sanjiv.ejb.UsersBeanRemote");
Users user = new Users();
user.setUserName("Sanjiv Dutta");
user.setEmailId("duttamca@gmail.com");
usersBean.insert(user);
System.out.println(usersBean.getUsers());
}
}
Once you run it this is what you see on the console. It inserted one user if the table and the loaded it from the table and displayed on the console. Here is my sample output from the console-
[1 Sanjiv Dutta duttamca@gmail.com]
Try inserting some more records by changing the username and emailid values. You can also cross check it from you database table.
Thanks for reading.

No comments:
Post a Comment