COOKBOOK Mysql xdoclet CMP key generator example
cgraham Jul 30, 2004 2:48 PMI have found it very hard to find a fully documented example that will help newbies do the basic stuff, so every time I find the solution to a problem that I have experienced that has taken me some time to solve I create a COOKBOOK.
This cookbook goes through the step by step process of creating a simple CMP using xdoclet for JBOSS and Mysql where the CMP will create a new primary key for every new entity.
1. You will need to deploy your data source for the MySQL database you wish to use. This is configured in mysql-ds.xml in the deploy dir. If you can use the real machine name for the datasource even if it is on localhost I have seen problems. If you are smart enough to remove this problem then all power to you.
<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>noiDS</jndi-name> <connection-url>jdbc:mysql://corbu/noi</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>jboss</user-name> <password>password</password> <min-pool-size>0</min-pool-size> </local-tx-datasource> </datasources>
2. You will need to configure the way JBOSS entity command for CMP creation to use the MySQL generator. To do this you will need to edit the standardjbosscmp-jdbc.xml in the conf dir. REMEMBER it is important to use the right DTD for the version of JBOSS you are using.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jbosscmp-jdbc PUBLIC "-//JBoss//DTD JBOSSCMP-JDBC 3.2//EN" "http://www.jboss.org/j2ee/dtd/jbosscmp-jdbc_3_2.dtd"> <!-- ===================================================================== --> <!-- --> <!-- Standard JBossCMP-JDBC Configuration --> <!-- --> <!-- ===================================================================== --> <!-- $Id: standardjbosscmp-jdbc.xml,v 1.39.2.34 2003/11/25 01:48:16 ejort Exp $ --> <jbosscmp-jdbc> <defaults> <datasource>java:/noiDS</datasource> <datasource-mapping>mySQL</datasource-mapping> <create-table>true</create-table> <remove-table>false</remove-table> <read-only>false</read-only> <read-time-out>300000</read-time-out> <row-locking>false</row-locking> <pk-constraint>true</pk-constraint> <fk-constraint>false</fk-constraint> <preferred-relation-mapping>foreign-key</preferred-relation-mapping> <read-ahead> <strategy>on-load</strategy> <page-size>1000</page-size> <eager-load-group>*</eager-load-group> </read-ahead> <list-cache-max>1000</list-cache-max> <unknown-pk> <unknown-pk-class>java.lang.Integer</unknown-pk-class> <field-name>id</field-name> <column-name>id</column-name> <jdbc-type>INTEGER</jdbc-type> <sql-type>INT(11)</sql-type> <auto-increment/> </unknown-pk> <entity-command name="mysql-get-generated-keys"/> </defaults>
3. Create the mySQL table that will map into your entity. It is important to note that the primary key field needs to be set to auto_increment. Commonly if you set xdoclet to create the table when the bean is deployed the auto increment *will not* be set so take care because the mysql-get-generated-keys command wont work unless this is set. (will always return 0).
# # Table structure for table `Job` # CREATE TABLE Job ( id int(11) NOT NULL auto_increment, name varchar(250) binary default NULL, description varchar(250) binary default NULL, creationDate datetime default NULL, PRIMARY KEY (id) ) TYPE=MyISAM;
4. You will need to create a XDOCLET 1.2.1 bean that will ultimately be the CMP to deploy. I have a simple bean that does this called job bean. It really does just act as a stub to prove that it works. Make sure to set the create table to false because you have already created your table. NOTE: If you want to have the bean create teh table make sure to go back to the mysql admin and add the auto_increment option to the primary key.
package com.noi.jbossmq.ejb.ds;
import javax.ejb.*;
import java.util.Date;
import java.util.Calendar;
import java.util.Collection;
import javax.naming.*;
import java.rmi.*;
import javax.rmi.PortableRemoteObject;
/**
* This is a job bean. It created using XDoclet tags.
*
* @ejb.bean
* type="CMP"
* name="Job"
* jndi-name="comp/ejb/Job"
* description="The Job Entity EJB"
* display-name="Job"
* primkey-field="id"
*
* @ejb.pk generate="true"
*
* @ejb.value-object
* match="*"
* name="Job"
*
* @ejb.finder
* signature="java.util.Collection findAll()"
* query="SELECT OBJECT(c) FROM Job c WHERE c.id > 0"
* transaction-type="Supports"
*
* @ejb.persistence
* table-name="Job"
*
* @ejb.interface remote-class="com.noi.jbossmq.ejb.ds.Job"
*
* @ejb.transaction
* type="Required"
*
*
* @jboss.create-table "false"
* @jboss.remove-table "false"
*
*
* @jboss.finder-query
* name="findAll"
* signature="java.util.Collection findAll()"
* query="SELECT OBJECT(c) FROM Job c WHERE c.id > 0"
*
* @jboss.entity-command
* name="mysql-get-generated-keys"
*
*
*/
public abstract class JobBean extends BaseEntityBean implements EntityBean {
public EntityContext mContext;
/**
* Id of this job.
*
* This is not remote since the primary key can be extracted by other means.
*
* @ejb.pk-field
* @ejb.persistent-field
* @ejb.interface-method
*
* @ejb.persistence column-name="id"
*
* @jboss.auto-increment
*
*
*/
public abstract Integer getId();
/**
* Id of this product.
*
*/
public abstract void setId(Integer id);
/**
* Name of the product.
*
* @ejb.interface-method
* @ejb.persistent-field
* @ejb.transaction type="Supports"
* @jboss.column-name "name"
*
*/
public abstract String getName();
/**
* ejb:interface-method
*/
public abstract void setName(String name);
/**
* Description of the product.
*
* @ejb.interface-method
* @ejb.persistent-field
* @ejb.transaction type="Supports"
* @jboss.column-name "description"
*
*/
public abstract String getDescription();
/**
* ejb:interface-method
*/
public abstract void setDescription(String description);
/**
* Generated bulk accessor. This is set as remote to allow clients to get all data in one call.
*
* @ejb.interface-method
* @ejb.transaction
* type="Supports"
*/
public abstract JobData getData();
/**
* Generated bulk accessor. This is set as remote to allow clients to get all data in one call.
*
* @ejb.interface-method
* @ejb.transaction
* type="Supports"
*/
public abstract void setData(JobData data);
/**
* @ejb.create-method
*/
public Object ejbCreate( )
throws CreateException
{
setCreationDate(new Date());
return null;
}
/**
* @ejb.create-method
*/
public Object ejbCreate(
JobData data
)
throws CreateException{
try{
setId(data.getId());
setName(data.getName());
setDescription(data.getDescription());
setCreationDate(new Date());
}
catch(Exception e)
{
throw new CreateException(e.getMessage());
}
return null;
}
public void ejbPostCreate( )
throws CreateException {}
public void ejbPostCreate(
JobData data
)
throws CreateException
{
}
public void setEntityContext( EntityContext lContext ) {mContext = lContext;}
public void unsetEntityContext(){ mContext = null; }
public void ejbActivate(){}
public void ejbPassivate(){}
public void ejbLoad(){}
public void ejbStore(){}
// EntityBean implementation -------------------------------------
/**
* Remove
*
* @ejb.transaction type="Mandatory"
*/
public void ejbRemove() throws RemoveException {}
}
*********************
package com.noi.jbossmq.ejb.ds;
import javax.ejb.*;
/**
* SuperClass for all entity beans, implementing common entity bean methods.
*
* @author <a href="mailto:youremail@yourdomain.com">youremail@yourdomain.com</a>
* @version $Revision: 1.6 $
*/
public abstract class BaseEntityBean {
/** Reference to EntityContext. */
protected EntityContext entityContext = null;
/**
* The creation-date of the entity. This field is purely to track when
* this entity was created, and should be set in ejbCreate
* (<code>setCreationDate(new Date());</code>.
* It is not included in the value object.
*
* <p>We use the qualified name here because XDoclet doesn't copy imports from
* base classes into the generated interfaces.</p>
*
* @ejb.persistence column-name="creationDate"
* @ejb.interface-method
*
* @ejb.value-object exclude="true" match="*"
*
*/
public abstract java.util.Date getCreationDate();
public abstract void setCreationDate(java.util.Date creationDate);
/**
* Gets the EntityContext. To be used by classes extending this.
* @return the EntityContext of the EJB
*/
protected EntityContext getEntityContext() {
return entityContext;
}
/** Required to implement EntityBean. Sets the EntityContext. */
public void setEntityContext(EntityContext entityContext) throws EJBException {
this.entityContext = entityContext;
}
/** Required to implement EntityBean. Sets the EntityContext to null. */
public void unsetEntityContext() throws EJBException {
entityContext = null;
}
/** Required to implement EntityBean. Not implemented. */
public void ejbActivate() throws EJBException { }
/** Required to implement EntityBean. Not implemented. */
public void ejbPassivate() throws EJBException { }
/** Required to implement EntityBean. Not implemented. */
public void ejbLoad() throws EJBException { }
/** Required to implement EntityBean. Not implemented. */
public void ejbStore() throws EJBException { }
/** Required to implement EntityBean. Not implemented. */
public void ejbRemove() throws RemoveException, EJBException { }
}
5. The client needs to create the ejb and then set the data. My client is a MDB that converts a JMS text message and then persists the Job entity.
private void makeJob(Message message)
{
try{
TextMessage textMessage = null;
textMessage = (TextMessage)message;
String queryObjName = "comp/ejb/Job";
Context ctx = new InitialContext();
Object objref = ctx.lookup(queryObjName);
JobHome home = (JobHome)PortableRemoteObject.narrow(objref, JobHome.class);
System.out.println("message bean attempts to create.");
Job newjob = home.create();
JobData data = newjob.getData();
data.setName(textMessage.getText());
data.setDescription("A JOB");
newjob.setData(data);
}
catch(Exception e){
String emessage = e.getMessage();
String errorClass = e.getClass().getName();
System.out.println("class: "+ errorClass+" exception:"+emessage);
}
}
I *think* this pretty much covers it. I really hope I didn't leave anything out but if I did I am very sorry. Good luck because I know how hard it is to find answers to simple things sometimes.
Clay