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