summaryrefslogtreecommitdiffstats
path: root/docs/01_model_classes.mkd
blob: cd52c2bf7643eb085b300ebff62be061272b4f5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
## Model Classes
A model class represents a single table within your database.  Fields within your model class represent columns in the table.  The object types of your fields are reflectively mapped to SQL types by iciql at runtime.

Models can be manually written using one of two approaches: *annotation configuration* or *interface configuration*.  Both approaches can be used within a project and both can be used within a single model class, although that is discouraged.

Alternatively, model classes can be automatically generated by iciql using the model generation tool.  Please see the [tools](tools.html) page for details.

### Configuration Requirements and Limitations

1. Your model class **must** provide a public default constructor.
2. All columns are assumed NULLABLE unless explicitly set *@IQColumn(nullable = false)*. 
3. Only the specified types are supported.  Other types such as arrays and custom types are not supported.
4. Triggers, views, and other advanced database features are not supported.

### Fully Supported Data Types
The following data types can be used for all iciql expressions.
<table>
<tr><th colspan="2">Standard SQL Types</th></tr>
<tr><td>java.lang.String</td>
<td>VARCHAR *(length > 0)* or CLOB *(length == 0)*</td></tr>
 	
<tr><td>java.lang.Boolean</td>
<td>BIT</td></tr>
	
<tr><td>java.lang.Byte</td>
<td>TINYINT</td></tr>
	
<tr><td>java.lang.Short</td>
<td>SMALLINT</td></tr>
	
<tr><td>java.lang.Integer</td>
<td>INT</td></tr>
	
<tr><td>java.lang.Long</td>
<td>BIGINT</td></tr>
	
<tr><td>java.lang.Float</td>
<td>REAL</td></tr>
	
<tr><td>java.lang.Double</td>
<td>DOUBLE</td></tr>
	
<tr><td>java.math.BigDecimal</td>
<td>DECIMAL *(length == 0)* or DECIMAL(length,scale) *(length > 0)*</td></tr>
	
<tr><td>java.sql.Date</td>
<td>DATE</td></tr>
	
<tr><td>java.sql.Time</td>
<td>TIME</td></tr>
	
<tr><td>java.sql.Timestamp</td>
<td>TIMESTAMP</td></tr>

<tr><td>java.util.Date</td>
<td>TIMESTAMP</td></tr>

<tr><td>java.lang.Enum.name()<br/>*default type*</td>
<td>VARCHAR *(length > 0)* or CLOB *(length == 0)*<br/>*EnumType.NAME*</td></tr>

<tr><td>java.lang.Enum.ordinal()</td>
<td>INT<br/>*EnumType.ORDINAL*</td></tr>

<tr><td>java.lang.Enum implements<br/>*com.iciql.Iciql.EnumId.enumId()*</td>
<td>INT<br/>*EnumType.ENUMID*</td></tr>

<tr><th colspan="2">H2 Database Types</th></tr>
<tr><td>java.util.UUID</td>
<td>UUID</td><tr/>

</table>

**NOTE:**<br/>
The reverse lookup used for model generation, SQL type -> Java type, contains more mappings.<br/>
Please consult the `com.iciql.ModelUtils` class for details. 

### Partially Supported Data Types
The following data types can be mapped to columns for all general statements <u>BUT</u> these field types may **not** be used to specify **compile-time** clauses or constraints.

<table>
<tr><td>byte []</td>
<td>BLOB</td></tr>

<tr><td>boolean</td>
<td>BIT</td></tr>
	
<tr><td>byte</td>
<td>TINYINT</td></tr>
	
<tr><td>short</td>
<td>SMALLINT</td></tr>
	
<tr><td>int</td>
<td>INT</td></tr>
	
<tr><td>long</td>
<td>BIGINT</td></tr>
	
<tr><td>float</td>
<td>REAL</td></tr>
	
<tr><td>double</td>
<td>DOUBLE</td></tr>

</table>

#### Partially Supported Data Types Example
%BEGINCODE%
class Primitives {
    @IQColumn(primaryKey = true)
    int id;
        
    @IQColumn
    String name;
        
    public Primitives() {
    }
    
    public Primitives(int id, String name) {
        this.id = id;
        this.name = name;
    }    
}
    
Primitives p = new Primitives();

// the following expressions compile, but will throw iciql runtime exceptions
db.from(p).where(p.id).is(100).selectFirst();
db.from(p).where(p.id).atLeast(10).select();

// the following expressions will work as expected
db.from(p).select();
db.from(p).where("id = ?", 100).selectFirst();
db.from(p).where("id >= ?", 10).select();
db.insert(new Primitives(150, "test"));
db.update(new Primitives(150, "modified"));
db.delete(new Primitives(150, "test"));
%ENDCODE%


## Annotation Configuration
The recommended approach to setup a model class is to annotate the class and field declarations.

### advantages

- annotated fields may have any scope
- annotated fields may specify default values
- annotated models support annotated field inheritance making it possible to design a single base class that defines the fields and then create table subclasses that specify the table mappings.
- model runtime dependency is limited to the small, portable `com.iciql.Iciql` class file which contains the annotation definitions

### disadvantages

- more verbose model classes
- indexes are defined using "fragile" string column names
- compound primary keys are defined using "fragile" string column names

### default values

You may specify default values for an @IQColumn by either:

1. specifying the default value string within your annotation<br/>
%BEGINCODE%
// notice the single ticks!
@IQColumn(defaultValue="'2000-01-01 00:00:00'")
Date myDate;
%ENDCODE%
2. setting a default object on the field<br/>
%BEGINCODE%
@IQColumn
Date myDate = new Date(100, 0, 1);
%ENDCODE%

If you want to specify a database-specific variable or function as your default value (e.g. CURRENT_TIMESTAMP) you must do that within the annotation.  Also note that the IQColumn.defaultValue must be a well-formatted SQL DEFAULT expression whereas object defaults will be automatically converted to an SQL DEFAULT expression.
 
### Example Annotated Model
%BEGINCODE%
import com.iciql.Iciql.EnumType;
import com.iciql.Iciql.IQColumn;
import com.iciql.Iciql.IQEnum;
import com.iciql.Iciql.IQIndex;
import com.iciql.Iciql.IQTable;

@IQTable
@IQIndexes({
  @IQIndex({"productName", "category"}),
  @IQIndex(name="nameindex", value="productName")
})
public class Product {

	@IQEnum(EnumType.ORDINAL)
	public enum Availability {
		ACTIVE, DISCONTINUED;
	}

	@IQColumn(primaryKey = true)
	public Integer productId;
      
	@IQColumn(length = 200, trim = true)
	public String productName;
      
	@IQColumn(length = 50, trim = true)
	public String category;
      
	@IQColumn
	public Double unitPrice;
      
	@IQColumn(name = "units")
	public Integer unitsInStock;
      
	@IQColumn
	private Integer reorderQuantity;
	
	@IQColumn
	private Availability availability;
      
	public Product() {
		// default constructor
	}
}
%ENDCODE%

## Interface Configuration (deprecated)
Alternatively, you may map your model classes using the original JaQu interface approach by implementing the `com.iciql.Iciql` interface.

This is a less verbose configuration style, but it comes at the expense of introducing a compile-time dependency on the logic of the iciql library.  This might be a deterrent, for example, if you were serializing your model classes to another process that may not have the iciql library.

The `com.iciql.Iciql` interface specifies a single method, *defineIQ()*.  In your implementation of *defineIQ()* you would use static method calls to set:

- the table name (if it's not the class name)
- the column name (if it's not the field name)
- the max length of a string field
- the primaryKey (single field or compound)
- any indexes (single field or compound)

### advantages

- less verbose model class
- compile-time index definitions
- compile-time compound primary key definitions

### disadvantages

- <u>only **public** fields of the model class are reflectively mapped as columns</u>. all other scoped fields and inherited fields are ignored.
- model runtime dependency on entire iciql library
- *defineIQ()* is called from a static synchronized block which may be a bottleneck for highly concurrent systems

### Example Interface Model
%BEGINCODE%
import com.iciql.Iciql;

public class Product implements Iciql {
	public Integer productId;
	public String productName;
	public String category;
	public Double unitPrice;
	public Integer unitsInStock;
	
	// this field is ignored because it is not public
	Integer reorderQuantity;
      
	public Product() {
	}
      
	@Override
	public void defineIQ() {
		com.iciql.Define.primaryKey(productId);
		com.iciql.Define.columnName(unitsInStock, "units");
		com.iciql.Define.length(productName, 200);
		com.iciql.Define.length(category, 50);
		com.iciql.Define.index(productName, category);
	}
}
%ENDCODE%