前言
由于第二章是整个文档的核心,内容也很多,所以分次翻译。下一章的内容会继续本章接着翻译。
-------------------------------------------------------------------------------------
2 如何使用
这一章进入到更多详细地使用ORMLite的各种功能。
2.1 配置你的Class
为了配置你的class使其持久化,你需要做下面几步:
①添加@DatabaseTable 注解到你每个需要持久化的类的顶部。你也可以用@Entity。
②添加@DatabaseField 注解到你需要持久化的字段的上面。你也可以使用@Column和其他的。
③为每个class添加一个无参的构造器,并且构造器在包内是可见的。
2.1.1 添加ORMLite 注解
自从java5开始,注解是有效的特殊代码标识,它提供meta信息包括类、方法或成员变量。把指定的类和成员变量存入数据库,ORMLite既支持它自己的注解(@DatabaseTable和 @DatabaseField)也支持很多来自javax.persistence包中标准的注解。注解是配置你的class最简单的方式,当然你也可以使用java代码或者Spring xml(Spring是个框架,更多信息google一下)对class进行配置。
用ORMLite注解,对每个你想要持久化到SQL数据库的java类你都需要添加@DatabaseTable注解到public class 这一行的正上方。每个被这些注解标记过的class都将持久化到它自己的数据库表中。例如:
@DatabaseTable(tableName = "accounts") public class Account { ...
@DatabaseTable注解可以有个可选的tableName 参数,也就是这个类对于的表名。如果没有特别指出,那么这个类名将默认作为表名。使用示例中的Account的对象都将会作为一条记录持久化到数据库名为account的表中。这将会在DaoManager实例化内部Dao的时候使用到。
除此之外,对于每个class你需要添加@DatabaseField注解到class的成员变量上,这个类是需要持久化到数据库的。每个成员变量都将被作为数据库中记录的一个字段进行持久化。示例:
@DatabaseTable(tableName = "accounts") public class Account { @DatabaseField(id = true) private String name; @DatabaseField(canBeNull = false) private String password; ...
①name字段,它是一个字符串并且是数据库中记录的唯一标识(主键)。在上面的示例中,account表的每条记录都有两个字段:
②password字段,它也是一个字符串,它不能为null。
@DatabaseField注解可以用下面的一些成员:(对常用的字段进行翻译,其他的参考原文)
常用的注解 |
||
成员名 |
数据类型 |
描述 |
columnName |
String |
数据库的列名。如果你没有设置这个成员名,会用标准的形式代替它。 |
dataType |
字段的数据类型。通常情况下,数据类型是从java类的成员变量获取的,并不需要进行特殊指出。它相当于是SQL的数据类型。 |
|
defaultValue |
String |
当我们在表中创建新的记录时的一个字段的默认值。默认情况下是没有这个值的。 |
width |
Integer |
字段的宽度,主要用于字符串字段。默认是0,意味着采用默认的数据类型和具体的数据库的默认情况。对于字符串以为在255个字符即使有些数据库并不支持。 |
canBeNull |
Boolean |
字段是否能被分配null值。默认是true。如果你设置成false,那么你每次在数据库中插入数据是都必须为这个字段提供值。 |
id |
Boolean |
这个字段是否是id,默认是false。在一个class中只有一个成变量可以有这个值。id字段是一条记录的唯一标识而且是必需的,只有generatedId和 generatedIdSequence其中之一。 |
generatedId |
Boolean |
字段是否自动增加。默认为false。类中的一个成员变量设置了这个值,它告诉数据库每添加一条新记录都自动增加id。当一个有generatedid的对象被创建时使用Dao.create()方法,数据库将为记录生成一个id,它会被返回并且被create方法设置进对象。 |
generatedIdSequence |
String |
序列编号的名字,这个值在生成的时候会被使用。和generatedId相似,但是你能够指定使用的序列名称。默认是没有的。一个class中只有一个成员变量可以设置这个值。这仅仅在数据库需要序列生成id时才需要它。如果你选择使用generatedId代替它,那么代码将自动增加序列名。 |
其他注解 |
||
foreign |
throwIfNull |
|
useGetSet |
persisted |
|
unknownEnumName |
format |
|
uniqueIndexName |
allowGeneratedIdInsert |
|
foreignAutoRefresh |
columnDefinition |
|
unique |
uniqueIndex |
|
uniqueCombo |
indexName |
|
index |
uniqueIndexName |
|
version |
maxForeignAutoRefreshLevel |
|
foreignColumnName |
foreignAutoCreate |
2.1.2 使用javax.persistence 注解
取代使用ORMLite注解,你可以使用来自javax.persistence包的更多的标准JPA注解。取代@DatabaseTable注解,你可以使用javax.persistence @Entity注解。示例:
@Entity(name = "accounts") public class Account { ...
@Entity注解有个可选的name参数,它用于指定表名。如果没有指定,类名将是默认的表名。
在每个成员变量中取代使用@DatabaseField注解,你可以用javax.persistence注解: @Column, @Id, @GeneratedValue, @OneToOne,@ManyToOne, @JoinColumn, and @Version. 示例:
下面这些javax.persistence注解和字段都支持:
注解 |
注解属性 |
描述 |
@Entity |
name |
用于关联的数据库表的名字。如果没有设置那么类名将被作为表名。 |
@Column |
name |
用作表字段的名字。如果没有设置那么变量名将作为字段名。 |
length |
数据库表字段的长度。可能只有应用在字符串并且只被某些数据库类型支持。默认是255。 |
|
nullable |
设置成true,那么这个字段允许插入null值。 |
|
unique |
添加一个约束,它在表中必须是唯一的。 |
|
@GeneratedValue |
用于定义一个自动增长的id值,它只能用于添加到@Id注解。
|
|
@OneToOne |
成员变量使用这些注解后会被认为是外键字段。 ORMLite没有实现多个或一个关系,它也不能使用任何注解成员。它只能使用这些注解的两者之一来表明它是一个外键。 |
|
@ManyToOne |
||
@JoinColumn |
name |
设置成员变量的列名(字段名)。 |
nullable |
设置成true,那么这个字段允许插入null值。 |
|
@Version |
使用它将会把short, integer, long, Date这些类型的成员转化成版本成员。 |
如果@Column注解在成员变量上用了一个未知的类型,那么它将被认为是序列化类型字段并且这个对象需要实现java.io.Serializable。
2.1.3 添加无参构造器
在你给class添加了注解字段后,你也需要添加一个无参的包内可见的构造器。当一个对象在查询中被返回时,ORMLite使用java反射机制构造一个对象并且构造器需要被调用。所以你最终的示例拥有注解和构造器的Account类应该像这样:
@DatabaseTable(tableName = "accounts") public class Account { @DatabaseField(id = true) private String name; @DatabaseField(canBeNull = false) private String password; ... Account() { // all persisted classes must define a no-arg constructor // with at least package visibility } ... }
2.2 持久化数据类型
下面这些java类型能够被ORMLite持久化到数据库。数据库具体编码帮助SQL类型和数据库具体持有类型的相互转化。
(具体的类型相互转化在此就不作介绍了,参见原文)。
2.3 连接源
注意:关于连接源,android用户应该参见手册中Android详细文档。
为了使用数据库和DAO对象,你需要配置JDBC调用数据源和ORMLite调用连接源。连接源是连接物理SQL数据库的一个工厂。这里是创建简单、单连接的代码示例:
// single connection source example for a database URI ConnectionSource connectionSource = new JdbcConnectionSource("jdbc:h2:mem:account");
这包中也包括了类JdbcPooledConnectionSource ,它是一个相对简单的连接池的实现。一个数据库连接已经释放而成为关闭,之后向他们添加内部列表他们都会拒绝。只有在没有休眠的可用的连接时,新的连接才需要创建。JdbcPooledConnectionSource也是同步的并且可以用于多线程中。在连接关闭前它可以设置空闲连接的最大数和存活的最长时间。
// pooled connection source JdbcPooledConnectionSource connectionSource = new JdbcPooledConnectionSource("jdbc:h2:mem:account"); // only keep the connections open for 5 minutes connectionSource.setMaxConnectionAgeMillis(5 * 60 * 1000);
JdbcPooledConnectionSource也有一个一直存活的线程,它偶尔ping一下池中空闲的每个连接,目的是为了确认他们是有效的,关闭的那个就不再有效。在你从池中取得连接之前你也可以测试连接是否有效。
// change the check-every milliseconds from 30 seconds to 60 connectionSource.setCheckConnectionsEveryMillis(60 * 1000); // for extra protection, enable the testing of connections // right before they are handed to the user connectionSource.setTestBeforeGet(true);
有很多其他额外的数据,他们能够被使用,包括很多更强大甚至高性能的池连接管理器。你可以用你自己直接封装的DataSourceConnectionSource类来例举说明。
// basic Apache data source BasicDataSource dataSource = new BasicDataSource(); String databaseUrl = "jdbc:h2:mem:account"; dataSource.setUrl(databaseUrl); // we wrap it in the DataSourceConnectionSource ConnectionSource connectionSource = new DataSourceConnectionSource(dataSource, databaseUrl);
当你用ConnectionSource时,你想调用close()方法来关闭一些底层的连接。推荐像下面这样的模式。
JdbcConnectionSource connectionSource = new JdbcPooledConnectionSource("jdbc:h2:mem:account"); try { // work with the data-source and DAOs ... } finally { connectionSource.close(); }
很不幸,DataSource接口没有关闭方法,所以你使用DataSourceConnectionSource你必须关闭底层数据源,通过操作DataSourceConnectionSource上的close()方法。
2.4 配置DAOs
一旦在你的类中有注解并且定义了你的ConnectionSource,你就需要创建一个DAO(Data Access Object),它是一个拥有数据库操作句柄的单一持久化类。每个DAO都有两个泛型参数:①我们用DAO持久化的类,②id字段,它用于确定数据库具体的记录。如果你的类没有ID字段,你可以放入Object或者Void作为第二个参数。例如,在上面的Account类,成员变量name是ID字段,所以ID类是String。
创建DAO最简单的方式是使用DaoManager类的静态方法createDao。示例:
Dao<Account, String> accountDao = DaoManager.createDao(connectionSource, Account.class); Dao<Order, Integer> orderDao = DaoManager.createDao(connectionSource, Order.class);
注意:你需要使用DaoManager.createDao()方法创建你自己的DAO类,所以如果内置ORMLite功能是需要他们,他们可以被再次利用并且不能再次生成。创建DAO会有昂贵的操作代价并且很多设备有资源限制(比如移动设备应用),尽可能重复使用DAO。
如果你想更好的类层次的机构或者你需要添加附加的方法套你的DAOs中,你应该考虑定义一个接口,它继承自Dao接口。这个接口不是必需的,但是他说一种好的模式,这样你的代码在关联JDBC持久化的时候会更少。接下来是一个相当于本手册前面章节Account类的DAO接口的示例:
/** Account DAO which has a String id (Account.name) */ public interface AccountDao extends Dao<Account, String> { // empty wrapper, you can add additional DAO methods here }
然后在实现中,你需要扩展BaseDaoImpl基类。这里是个实现你的DAO接口的示例。
/** JDBC implementation of the AccountDao interface. */ public class AccountDaoImpl extends BaseDaoImpl<Account, String> implements AccountDao { public AccountDaoImpl(ConnectionSource connectionSource) throws SQLException { super(connectionSource, Account.class); } }
那就是你需要定义你的DAO类。如果有特殊的操作需要并且Dao基类没有提供的方法,你可以自由添加更多方法到你的DAO接口和添加到你的实现中。
注意:如果你正在使用一个定制的DAO,然后确保添加daoClass参数到你自己定制的DAO类的@DatabaseTable注解。这会被DaoManager用于内部实例化DAO。
2.5 支持的数据库
ORMLite支持下面的数据库。这些数据库中的某些数据库有具体需要遵守的文档。(下面给出支持的数据库,具体文档参见官方文档)
支持的数据库 |
|||||
MySQL |
H2 |
Android SQLite |
HSQLDB |
Netezza |
DB2 |
Postgres |
SQLite |
Microsoft SQL Server |
Derby |
ODBC |
Oracle |
2.6 整合
这样你有一个注解对象被持久化,添加一个无参构造器,创建你的ConnectionSource并且定义你的DAO类。你已经开始持久化和查询你的数据库对象了。你需要下载并且添加H2 jar文件到你的classPath中,如果你想让这个示例跑起来的话。下面是整合的代码:
// h2 by default but change to match your database String databaseUrl = "jdbc:h2:mem:account"; JdbcConnectionSource connectionSource = new JdbcConnectionSource(databaseUrl); // instantiate the dao with the connection source AccountDaoImpl accountDao = new AccountDaoImpl(connectionSource); // if you need to create the 'accounts' table make this call TableUtils.createTable(connectionSource, Account.class); // create an instance of Account Account account = new Account("Jim Coakley"); // persist the account object to the database accountDao.create(account); ... // destroy the data source which should close underlying connections connectionSource.destroy();
PS: 第二章还在翻译中... 文中有不妥之处希望读者提出,转载请注明出处。