static void main (String [] args) {...}I'll start to use static void main (String ... args){...} instead. It's a sing of a new era established by Java 5 ...
static void main (String [] args) {...}I'll start to use static void main (String ... args){...} instead. It's a sing of a new era established by Java 5 ...
package test;
public interface IntA {
void A();
void B();
void C();
void D();
void E();
}
package test;
public class ClsB implements IntA{
public void A() {
System.out.println("A");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Howdyyyy");
IntA a = new ClsB();
a.A();
// a.B(); when uncommented, class doesn't compile
}
}
K:\UFO_G3\workspace\test>c:\jdk5\bin\javac.exe test/*.java
test/ClsB.java:3: test.ClsB is not abstract and does not override abstract metho
d E() in test.IntA
public class ClsB implements IntA{
^
1 error
As you can see, it's very straightward - nothing is hidden and, there is no logic ! No methods, except for constructor. But if you take a closer look on OneToMany mapping you can realize that there must be some kind of executive code out there. And it certainly is. Entity manager hides further logic behind the Collection of Departments. This logic is responsible for lazy fetching of Departments. As long as you run this code inside the container (ie. Glassfish) everything is all right but once you try to send such POJO over let's say webservice you can encounter a serious problem. As far as I'm aware there's no way how to detach such POJO and attached POJO can't be detached by SOAP message creator. And that's the reason why I have created second version of EJBs. In the next example you'll see the same table (Employees) but without any relationship included:
package org.defectus.ejb3.ic2.entity.complex;
import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "EMPLOYEES")
@NamedQuery(name = "selectAllEmps", query = "select object(o) from Employees o")
public class Employees implements Serializable {
private static final long serialVersionUID = 1987305452306161213L;
private Double commissionPct;
private String email;
private Long employeeId;
private String firstName;
private String lastName;
private String phoneNumber;
private Double salary;
private Departments department;
private Employees manager;
private Collectionemployees;
private Jobs jobs;
private Collectiondepartments;
public Employees() {
}
public Employees(String email, Long employeeId, Jobs jobs, String lastName) {
this.email = email;
this.employeeId = employeeId;
this.jobs = jobs;
this.lastName = lastName;
}
@Column(name = "COMMISSION_PCT")
public Double getCommissionPct() {
return commissionPct;
}
public void setCommissionPct(Double commissionPct) {
this.commissionPct = commissionPct;
}
@Column(name = "EMAIL", nullable = false)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Id()
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EMPLOYEES_SEQ")
@Column(name = "EMPLOYEE_ID", nullable = false)
public Long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}
@Column(name = "FIRST_NAME")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Column(name = "LAST_NAME", nullable = false)
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Column(name = "PHONE_NUMBER")
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Column(name = "SALARY")
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@ManyToOne()
@JoinColumn(name = "DEPARTMENT_ID", referencedColumnName = "DEPARTMENTS.DEPARTMENT_ID")
public Departments getDepartment() {
return department;
}
public void setDepartment(Departments departments) {
this.department = departments;
}
@ManyToOne()
@JoinColumn(name = "MANAGER_ID", referencedColumnName = "EMPLOYEES.EMPLOYEE_ID")
public Employees getManager() {
return manager;
}
public void setManager(Employees manager) {
this.manager = manager;
}
@OneToMany()
public CollectiongetEmployees() {
return employees;
}
public void setEmployees(Collectionemployees) {
this.employees = employees;
}
public Employees addToEmployeesCollection(Employees employees) {
getEmployees().add(employees);
employees.setManager(this);
return employees;
}
public Employees removeFromEmployeesCollection(Employees employees) {
getEmployees().remove(employees);
employees.setEmployees(null);
return employees;
}
@ManyToOne()
@JoinColumn(name = "JOB_ID", referencedColumnName = "JOBS.JOB_ID")
public Jobs getJobs() {
return jobs;
}
public void setJobs(Jobs jobs) {
this.jobs = jobs;
}
@OneToMany()
public CollectiongetDepartments() {
return departments;
}
public void setDepartments(Collectiondepartments) {
this.departments = departments;
}
public Departments addToDepartmentsCollection(Departments departments) {
getDepartments().add(departments);
departments.setManager(this);
return departments;
}
public Departments removeFromDepartmentsCollection(Departments departments) {
getDepartments().remove(departments);
departments.setManager(null);
return departments;
}
}
In this example you perhaps lack the relationship annotations and structures but a native query has been add. Note that EJB3 easily fall to annotation defaulting. This means that even if you omit annotate class field as Column deployer will find that in related table is matching column and will bind tthe column and the filed together.
package org.defectus.ejb3.ic2.entity.bare;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQuery;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.Table;
@Entity
@Table(name = "EMPLOYEES")
@NamedQuery(name = "selectAllEmps", query = "select object(o) from Employees o")
@NamedNativeQuery(name = "selectNativeEmps", query = "SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, SALARY FROM EMPLOYEES WHERE EMPLOYEE_ID = ?", resultSetMapping = "employeeNamedMapping", resultClass=org.defectus.ejb3.ic2.entity.bare.BareEmployees.class)
@SqlResultSetMapping(name = "employeeNamedMapping", entities = { @EntityResult(entityClass = org.defectus.ejb3.ic2.entity.bare.BareEmployees.class, fields = {
@FieldResult(name = "employeeId", column = "EMPLOYEE_ID"), @FieldResult(name = "firstName", column = "FIRST_NAME"),
@FieldResult(name = "lastName", column = "LAST_NAME"), @FieldResult(name = "salary", column = "SALARY") }) /*, discriminatorColumn="disc")*/})
public class BareEmployees implements Serializable {
private static final long serialVersionUID = 1987305452306161213L;
private Double commissionPct;
private String email;
private Long employeeId;
private String firstName;
private String lastName;
private String phoneNumber;
private Double salary;
private Long departments;
private Long employees;
private String jobs;
public BareEmployees() {
}
public BareEmployees(String email, Long employeeId, String jobs, String lastName) {
this.email = email;
this.employeeId = employeeId;
this.jobs = jobs;
this.lastName = lastName;
}
@Column(name = "COMMISSION_PCT")
public Double getCommissionPct() {
return commissionPct;
}
public void setCommissionPct(Double commissionPct) {
this.commissionPct = commissionPct;
}
@Column(name = "EMAIL", nullable = false)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Id()
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EMPLOYEES_SEQ")
@Column(name = "EMPLOYEE_ID", nullable = false)
public Long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}
@Column(name = "FIRST_NAME")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Column(name = "LAST_NAME", nullable = false)
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Column(name = "PHONE_NUMBER")
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
@Column(name = "SALARY")
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Column(name = "DEPARTMENT_ID")
public Long getDepartments() {
return departments;
}
public void setDepartments(Long departments) {
this.departments = departments;
}
@Column(name = "MANAGER_ID")
public Long getEmployees() {
return employees;
}
public void setEmployees(Long employees) {
this.employees = employees;
}
@Column(name = "EMPLOYEES.JOB_ID")
public String getJobs() {
return jobs;
}
public void setJobs(String jobs) {
this.jobs = jobs;
}
}
For the bean a remote interface is needed. To create one you have to annotate appropriate interface as @Remote and make the class above implement such interface. Note that the interface can be Remote, Local but can't be both.
package org.defectus.ejb3.ic2.session;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import javax.ejb.Interceptors;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.defectus.ejb3.ic2.entity.bare.BareDepartments;
import org.defectus.ejb3.ic2.entity.bare.BareEmployees;
import org.defectus.ejb3.ic2.entity.bare.BareJobs;
import org.defectus.ejb3.ic2.entity.bare.BareLogs;
import org.defectus.ejb3.ic2.entity.complex.Departments;
import org.defectus.ejb3.ic2.entity.complex.Employees;
import org.defectus.ejb3.ic2.entity.complex.Locations;
@Stateless (mappedName="ejb/DataFacade")
@Interceptors( { org.defectus.ejb3.ic2.session.DataInterceptor.class })
public class DataFacadeBean implements DataFacade {
@PersistenceContext(unitName = "pu1")
private EntityManager _entityManager;
public DataFacadeBean() {
}
public EntityManager getEntityManager() {
return _entityManager;
}
public void setEntityManager(EntityManager entityManager) {
_entityManager = entityManager;
}
public ListfindDept(String name) {
@SuppressWarnings(value = { "unchecked" })
Listdeps = getEntityManager().createQuery(
"select object(o) from Departments o where o.departmentName=:name").setParameter("name", name).getResultList();
return deps;
}
public ListfindAllDept() {
@SuppressWarnings(value = { "unchecked" })
Listdeps = getEntityManager().createQuery("select object(o) from Departments o").getResultList();
return deps;
}
public Departments createDepartment(String departmentName, Long locationId) {
final Departments dept = new Departments(departmentName);
Locations location = getEntityManager().find(Locations.class, locationId);
dept.setLocation(location);
getEntityManager().persist(dept);
return dept;
}
public BareLogs createLog(String message) {
final BareLogs log = new BareLogs(message, new Date());
getEntityManager().persist(log);
return log;
}
public ListfindAllComplexEmployees() {
@SuppressWarnings(value = { "unchecked" })
Listemps = getEntityManager().createNamedQuery("selectAllEmps").getResultList();
return emps;
}
public ListfindAllBareEmployees() {
@SuppressWarnings(value = { "unchecked" })
Listemps = getEntityManager().createQuery("select object(o) from BareEmployees o").getResultList();
return emps;
}
public ListfindBareEmployees2() {
@SuppressWarnings(value = { "unchecked" })
Listemp = getEntityManager().createNamedQuery("selectNativeEmps").setParameter(1,101).getResultList();
return emp;
}
public BareJobs findBareJobs(String jobId) {
return getEntityManager().find(BareJobs.class, jobId);
}
public BareEmployees findBareEmployee(Long emplId) {
return getEntityManager().find(BareEmployees.class, emplId);
}
public BareDepartments findBareDepartment(Long depId) {
return getEntityManager().find(BareDepartments.class, depId);
}
public ListfindAllDepartments() {
@SuppressWarnings(value = { "unchecked" })
Listdeps = getEntityManager().createNamedQuery("selectAllDeps").getResultList();
return deps;
}
public Employees createEmployee(Double comm, Long deptno, String email, String firstName, String lastName,
Timestamp hiredate, String job, Double sal) {
final Employees emp = new Employees();
Departments dep = getEntityManager().find(Departments.class, deptno);
emp.setCommissionPct(comm);
emp.setDepartment(dep);
emp.setEmail(email);
emp.setFirstName(firstName);
emp.setLastName(lastName);
emp.setSalary(sal);
getEntityManager().persist(emp);
return emp;
}
public void updateEntity(Object entity) {
getEntityManager().merge(entity);
}
public void deleteEntity(Object entity) {
final EntityManager em = getEntityManager();
em.remove(em.merge(entity)); // interesting way how to delete entity of any type
}
public void refreshEntity(Object entity) {
getEntityManager().refresh(entity);
}
public void shutdown() {
getEntityManager().close();
}
}
Interceptor is something that is called before any particular call to method of intercepted bean. From the code below it's easy to understand the way of interception
package org.defectus.ejb3.ic2.session;
import java.sql.Timestamp;
import java.util.List;
import javax.ejb.Remote;
import javax.persistence.EntityManager;
import org.defectus.ejb3.ic2.entity.bare.BareEmployees;
import org.defectus.ejb3.ic2.entity.bare.BareJobs;
import org.defectus.ejb3.ic2.entity.bare.BareLogs;
import org.defectus.ejb3.ic2.entity.complex.Departments;
import org.defectus.ejb3.ic2.entity.complex.Employees;
@Remote
public interface DataFacade {
public abstract EntityManager getEntityManager();
public abstract void setEntityManager(EntityManager entityManager);
public abstract ListfindAllDept();
public abstract ListfindDept(String name);
public abstract Departments createDepartment(String departmentName, Long locationId);
public abstract BareLogs createLog(String message);
public abstract ListfindAllBareEmployees();
public abstract BareJobs findBareJobs(String jobId);
public abstract BareEmployees findBareEmployee(Long emplId);
public abstract ListfindAllComplexEmployees();
public abstract ListfindAllDepartments();
public abstract Employees createEmployee(Double comm, Long deptno, String email, String firstName, String lastName,
Timestamp hiredate, String job, Double sal);
public abstract void updateEntity(Object entity);
public abstract void deleteEntity(Object entity);
public abstract void refreshEntity(Object entity);
public abstract void shutdown();
}
The second IF is interesting - because it interrupts call to underlaying method so the method is actually not called at all ! This means - when you ask the bean for employee 888 interceptor returns one without even calling datafacade bean. Very nice. Note that specification have changed and Intercaption resides in different package (but it works the same way as before).In the next class you'll see is a servlet that acts as data provider to AJAX page. Although servlets are not annotated (you must provide valid web.xml) you can use annotations though. Annotation @EJB instructs server to inject reference to EJB. The rest of the class is servlet as usual. Just for your information, the last Java EE 5 specification prohibit usage of stateful bean in servlets.
package org.defectus.ejb3.ic2.session;
import javax.ejb.AroundInvoke;
import javax.ejb.InvocationContext;
import org.defectus.ejb3.ic2.entity.bare.BareEmployees;
public class DataInterceptor {
@AroundInvoke()
public Object myBeanInterceptor(InvocationContext ctx) throws Exception {
if (ctx.getMethod().getName().equals("findDept")) {
String name = (String) ctx.getParameters()[0];
Object[] o = new Object[1];
o[0] = name;
ctx.setParameters(o);
} else if (ctx.getMethod().getName().equals("findBareEmployee")) {
Long id = (Long) ctx.getParameters()[0];
if (id.longValue() == 888)
return new BareEmployees(null, null, null, "Who wants to know ? :-)");
}
return ctx.proceed();
}
}
And that's all for now. I'll continue later and try to cover another techniques related to EJB 3.
package org.defectus.ejb3.ic2.ws;
import java.io.IOException;
import java.util.Map;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.defectus.ejb3.ic2.entity.bare.BareEmployees;
import org.defectus.ejb3.ic2.session.DataFacade;
public class DataProvider extends HttpServlet {
private static final long serialVersionUID = -7542313195870441831L;
@EJB(name = "org.defectus.ejb3.ic2.session.DataFacade")
private DataFacade data;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
ServletOutputStream o = res.getOutputStream();
res.setContentType("text/xml");
res.setCharacterEncoding("utf-8");
String action = null;
Long id = null;
try {
action = req.getParameter("action");
} catch (Exception e) {
o.println(""); ");
o.println(""); ");
o.println("Invalid request !!! No action provided");
o.println("
o.println("
o.flush();
o.close();
return;
}
Map params = req.getParameterMap();
if (action.equals("singleEmployee")) {
printSingleEmployee(o, Long.getLong((String) params.get("id")));
} else if (action.equals("employeeList")) {
printEmployeeList(o);
}
}
private void printSingleEmployee(ServletOutputStream o, Long value) throws IOException {
o.println(""); ");
o.println(""); ");
o.println("" + data.findBareEmployee(value).getLastName() + "");
o.println("
o.println("
o.flush();
o.close();
}
private void printEmployeeList(ServletOutputStream o) throws IOException {
o.println(""); ");
o.println(""); ");
o.println("");
o.println("
o.println("
o.flush();
o.close();
}
}
@ManyToOne()
//targetEntity=org.defectus.ejb3.ic2.entity.Departments.class)
@JoinColumn(name = "EMPLOYEES.DEPARTMENT_ID", referencedColumnName = "DEPARTMENTS.DEPARTMENT_ID")
Hope that is clear - commented parts are these which were generated by JDeveloper but were not valid.
@OneToMany(targetEntity=org.defectus.ejb3.ic2.entity.Employees.class)
//@JoinColumn(name="EMPLOYEES.DEPARTMENT_ID", referencedColumnName="DEPARTMENTS.DEPARTMENT_ID")
package com.blogspot.deftech.ejb3;For such class we need an interface - lets create it (nothing complicated)
@javax.ejb.Stateless (name="ejb/foo")
public class FooBean {
public String getString (String stringIn) {
return "You said " + stringIn + " ... Unbelievable !";
}
}
package com.blogspot.deftech.ejb3;
@javax.ejb.Remote
public interface Foo {
public String getString (String stringIn);
}
package com.blogspot.deftech.ejb3;To deploy such code you need to compile it and pack it into *.jar file. More on this later today. Be patient ...
public class FooTest {
@javax.ejb.EJB (name="ejb/foo")
private Foo fooObject;
public static void main (String [] args) {
FooTest test = new FooTest();
test.run();
}
private void run () {
System.out.println(fooObject.getString ("1+1=2"));
}
}
package com.blogspot.deftech.ejb3;In this piece of code application server will inject JNDI resource into setter named setDataSource. Really clean and elegant way how to work with JNDI. No more JNDI lookups, no more narrowing references. But still keep in mind, that on background nothing changes ! The only thing which has changed is that the boring part of work is done by server ... Later I'll post here example of entity beans, which are even more interesting. And also a small example of Web service. But don't expect anything revolutionary - just another set of annotations :-).
public class ResourceExample {
javax.sql.DataSource ds = null;
@Resource(name="jdbc/OracleXE")
public void setDataSource javax.sql.DataSource value) {
ds = value;
}
}