Advanced Customization > Services and Infrastructure Customization > System Generation > Modeling Business Objects > Modeling Tables
  
Modeling Tables
Modeled class anatomy (GenAsPersistable)
To begin with, here is a simple, functional example. Other sections within this chapter will refer back to this example.
Listing 1: SimpleExample.java
01 package com.acme.example;
02
03 import wt.fc.InvalidAttributeException;
04 import wt.fc.WTObject;
05 import wt.inf.container.WTContained;
06 import wt.util.WTException;
07 import wt.util.WTPropertyVetoException;
08
09 import com.ptc.windchill.annotations.metadata.*;
10
11 @GenAsPersistable(superClass=WTObject.class,
interfaces={WTContained.class},
12 properties={
13 @GeneratedProperty(name="name", type=String.class,
14 constraints=@PropertyConstraints(required=true))
15 })
16 public class SimpleExample extends _SimpleExample {
17 static final long serialVersionUID = 1;
18
19 public static SimpleExample newSimpleExample() throws WTException {
20 final SimpleExample instance = new SimpleExample();
21 instance.initialize();
22 return instance;
23 }
24
25 @Override
26 public void checkAttributes() throws InvalidAttributeException {
27 super.checkAttributes();
28 try {
29 nameValidate(name);
30 } catch (WTPropertyVetoException wtpve) {
31 throw new InvalidAttributeException(wtpve);
32 }
33 }
34 }
A class, SimpleExample (line 16) is defined which extends _SimpleExample. The class is annotated by the GenAsPersistable annotation (lines 11-15), which indicates that this class is to be persisted as a table in the database. The class has a single property called “name” (lines 13–14) which is a (required) String. Additionally, two methods have been implemented; a static factory method (lines 19-23) and an override of the checkAttributes() method (lines 25-33).
The source of the _SimpleExample is as follows:
The annotation processor for the GenAsPersistable annotation generates the parent class (more precisely , it generates a source file which the compiler then compiles).
The parent class provides an implementation (query constant, field, getter, setter, and setter validation) for the “name” property as well as externalization logic and other needed methods.
The “_” class consists of the fields and methods shown in Listing 2.
Listing 2: _SimpleExample javap results
01 public abstract class com.acme.example._SimpleExample extends wt.fc.WTObject implements wt.inf.container.WTContained,java.io.Externalizable{
02 static final long serialVersionUID;
03 static final java.lang.String RESOURCE;
04 static final java.lang.String CLASSNAME;
05 public static final java.lang.String NAME;
06 static int NAME_UPPER_LIMIT;
07 java.lang.String name;
08 wt.inf.container.WTContainerRef containerReference;
09 public static final long EXTERNALIZATION_VERSION_UID;
10 public com.acme.example._SimpleExample();
11 public java.lang.String getName();
12 public void setName(java.lang.String) throws wt.util.WTPropertyVetoException;
13 void nameValidate(java.lang.String) throws wt.util.WTPropertyVetoException;
14 public java.lang.String getContainerName();
15 public wt.inf.container.WTContainer getContainer();
16 public wt.inf.container.WTContainerRef getContainerReference();
17 public void setContainer(wt.inf.container.WTContainer) throws wt.util.WTPropertyVetoException, wt.util.WTException;
18 public void setContainerReference(wt.inf.container.WTContainerRef) throws wt.util.WTPropertyVetoException;
19 void containerReferenceValidate(wt.inf.container.WTContainerRef) throws wt.util.WTPropertyVetoException;
20 public java.lang.String getConceptualClassname();
21 public wt.introspection.ClassInfo getClassInfo() throws wt.introspection.WTIntrospectionException;
22 public java.lang.String getType();
23 public void writeExternal(java.io.ObjectOutput) throws java.io.IOException;
24 protected void super_writeExternal_SimpleExample(java.io.ObjectOutput) throws java.io.IOException;
25 public void readExternal(java.io.ObjectInput) throws java.io.IOException, java.lang.ClassNotFoundException;
26 protected void super_readExternal_SimpleExample(java.io.ObjectInput) throws java.io.IOException, java.lang.ClassNotFoundException;
27 public void writeExternal(wt.pds.PersistentStoreIfc) throws java.sql.SQLException, wt.pom.DatastoreException;
28 public void readExternal(wt.pds.PersistentRetrieveIfc) throws java.sql.SQLException, wt.pom.DatastoreException;
29 boolean readVersion6009937787959182077L(java.io.ObjectInput, long, boolean) throws java.io.IOException, java.lang.ClassNotFoundException;
30 protected boolean readVersion(com.acme.example.SimpleExample,
java.io.ObjectInput, long, boolean, boolean) throws java.io.IOException,
java.lang.ClassNotFoundException;
31 protected boolean super_readVersion_
SimpleExample
(com.acme.example._SimpleExample, java.io.ObjectInput, long, boolean, boolean) throws java.io.IOException, java.lang.ClassNotFoundException;
32 boolean readOldVersion(java.io.ObjectInput, long, boolean, boolean) throws java.io.IOException, java.lang.ClassNotFoundException;
33 static {};
34 }
The annotation consists of a declaration that is then implemented by the compiler. Rather than implement all aspects of “name” manually, it is declared as a property within the annotation and the “_” parent file implements all the necessary components. Listing 3 contains an example of the effect required=true has on the nameValidate() method.
Listing 3: nameValidate() snippet
01 if (name == null || name.trim().length() == 0)
02 throw new wt.util.WTPropertyVetoException("wt.fc.fcResource", wt.fc.fcResource.REQUIRED_ATTRIBUTE,
03 new Object[] { new wt.introspection.PropertyDisplayName (CLASSNAME, "name") },
04 new java.beans.PropertyChangeEvent(this, "name", this.name, name));
The purpose of superClass and interfaces (line 11, listing 1) is as follows:
The superClass annotation member is simple; extends was co-opted by the “_” class so modeled elements could be generated into code and incorporated into the class. Consequently, superClass is needed to allow you to specify your “true” (logical) parent (in this case, WTObject), which the “_” class (_SimpleExample) then extends for you (as seen above).
The interfaces annotation member exists for a similar purpose: to make it possible to identify the interfaces you wish to implement. While implements was not similarly co-opted, various technical considerations made an annotation member the logical choice over utilizing implements directly. This example extends WTObject and implements WTContained. The compiler will enforce the “public class <X> extends _<X> { ... } ” syntax and generates a compile error if this syntax is not used.
Note the use of the factory pattern (newSimpleExample() (line 19 of Listing 1). Windchill utilizes this pattern over constructors because externalization needs to construct an instance using the noarg constructor and calling it should be inexpensive. The factory pattern consists of a static method of the form “public static <X> new<X>(<args...>) throws WTException { ... }” and should be used in place of constructors in annotated files. The body of the factory should follow the form demonstrated by the example. Specifically, it should:
1. Construct an instance of the class using its (default) no-arg constructor.
2. Call the (non-static) initialize method, passing the arguments provided to the static method. The static factory should never handle the arguments on its own because then no subclass could take advantage of the static method’s work (this is the primary purpose of the initialize methods: to make it possible to inherit behavior). You may need to create your own initialize method (if one does not exist or you need to augment behavior), in which case it should be of the form protected void initialize(args...) throws WTException and should call super.initialize(...).
3. Return the instance.
The following factory/initialize pair demonstrates the proper way one would add a factory method accepting name so as to assign the required property.
Listing 4: Factory/initialize pair example
01 public static SimpleExample newSimpleExample(final String name) throws WTException {
02 final SimpleExample instance = new SimpleExample();
03 instance.initialize(name);
04 return instance;
05 }
06
07 protected void initialize(final String name) throws WTException {
08 super.initialize();
09 try {
10 setName(name);
11 } catch (WTPropertyVetoException wtpve) {
12 throw new WTException(wtpve);
13 }
14 }
Additionally, the serialVersionUID (line 17 of Listing 1) is necessary since Windchill manages externalization logic (including, as needed, old version deserialization) and system-assigned serial version UIDs would break this logic. As before, the compiler will generate a compile error if this is not provided.
Finally, checkAttributes() is notable only in that it demonstrates the use of generated fields and methods (line 29, Listing 1). In particular, name and nameValidate() are generated with default access specifically to make them accessible to the annotated class.
Compilation
Compilation is covered extensively in the JavaDoc for the com.ptc.windchill.annotations.metadata package. However, the SimpleExample customization can be compiled and incorporated by executing the following steps:
1. cd to your load point (for example, /opt/ptc/Windchill)
2. Start a Windchill shell by invoking bin/windchill shell
3. Create the src/com/acme/example directory via mkdir -p src/com/acme/example
4. Create SimpleExample.java in this directory and give it the contents of the example above
5. Compile the example with ant -f bin/tools.xml class -Dclass.includes=com/acme/example/* (note that this command must be executed in the load point directory)
6. Generate SQL scripts with ant -f bin/tools.xml sql_script -Dgen.input=com.acme.example.*
7. Find the create_SimpleExample_Table.sql SQL script (it will be somewhere in db) and load it
8. Repeat for create_SimpleExample_Index.sql.
9. Start/restart the MethodServer
Jython can be used to quickly validate the example, as shown in Listing 5.
Listing 5: Persisting SimpleExample
01 from wt.fc import PersistenceHelper
02 from com.acme.example import SimpleExample
03
04 se = SimpleExample.newSimpleExample()
05 se.setName('test')
06 se = PersistenceHelper.manager.store(se)
When prompted, authenticate as the system administrator. The object will be created in the site container, which can be confirmed via sn.getContainer().getName(). Note that, had we created the factory/initialize methods with name as an argument, we could have avoided calling setName(’test’) separately.
Mapping classes to tables and columns
The example persisted an instance of SimpleExample, resulting in the addition of a row in a table. Details of the table are shown in Listing 6.
Listing 6: create_SimpleExample_Table.sql
01 exec WTPK.dropTable('SimpleExample')
02 set echo on
03 REM Creating table SimpleExample for com.acme.example.SimpleExample
04 set echo off
05 CREATE TABLE SimpleExample (
06 classnamekeycontainerReferen VARCHAR2(600),
07 idA3containerReference NUMBER,
08 name VARCHAR2(600) NOT NULL,
09 createStampA2 DATE,
10 markForDeleteA2 NUMBER NOT NULL,
11 modifyStampA2 DATE,
12 classnameA2A2 VARCHAR2(600),
13 idA2A2 NUMBER NOT NULL,
14 updateCountA2 NUMBER,
15 updateStampA2 DATE,
16 CONSTRAINT PK_SimpleExample PRIMARY KEY (idA2A2))
17 STORAGE ( INITIAL 20k NEXT 20k PCTINCREASE 0 )
18 ENABLE PRIMARY KEY USING INDEX
19 TABLESPACE INDX
20 STORAGE ( INITIAL 20k NEXT 20k PCTINCREASE 0 )
21 /
22 COMMENT ON TABLE SimpleExample IS 'Table SimpleExample created for com.acme.example.SimpleExample'
23 /
24 REM @//com/acme/example/SimpleExample_UserAdditions
The table’s name reflects the name of the class (this can be changed – see the JavaDoc for the annotation member “tableProperties” and the associated member “tableName” for details). Of the columns, “name” is certainly recognizable, though the rest may not be. The classnamekeycontainerReferen and idA3containerReference columns are necessary to store the reference to the container, which we got by being WTContained. Everything else comes from being Persistable, which is the top-level interface for persisting Windchill classes as tables.
Modeled associations (GenAsBinaryLink)
GenAsPersistable is one of two annotations for mapping classes to tables. The other is GenAsBinaryLink, which represents an association, linking two persistent objects (rows in two tables) together. Each binary link consists of two roles, an “A” role and a “B” role.
We can create a graph of SimpleExamples, in which a SimpleExample may have multiple parents and children.
Listing 7: SimpleExampleLink.java
01 package com.acme.example;
02
03 import wt.fc.ObjectToObjectLink;
04 import wt.util.WTException;
05
06 import com.ptc.windchill.annotations.metadata.*;
07
08 @GenAsBinaryLink(superClass=ObjectToObjectLink.class,
09 roleA=@GeneratedRole(name="parent", type=SimpleExample.class),
10 roleB=@GeneratedRole(name="child", type=SimpleExample.class))
11 public class SimpleExampleLink extends _SimpleExampleLink {
12 static final long serialVersionUID = 1;
13
14 public static SimpleExampleLink newSimpleExampleLink
(final SimpleExample parent, final SimpleExample child)
throws WTException {
15 final SimpleExampleLink instance = new SimpleExampleLink();
16 instance.initialize(parent, child);
17 return instance;
18 }
19 }
After similarly compiling it, it can be validated with Jython, as shown in Listing 8.
Listing 8: Persisting SimpleExampleLink
01 from wt.fc import PersistenceHelper
02 from com.acme.example import *
03
04 parent = SimpleExample.newSimpleExample()
05 parent.setName('parent')
06 parent = PersistenceHelper.manager.store(parent)
07
08 child = SimpleExample.newSimpleExample()
09 child.setName('child')
10 child = PersistenceHelper.manager.store(child)
11
12 l = PersistenceHelper.manager.store(SimpleExampleLink.
newSimpleExampleLink (parent, child))
As with SimpleExample, we have a factory method (lines 14-17). Links, however, always associate two objects, so the factory method (and associated initialize method) accepts the A/B roles as arguments.