ORMLite是一款优秀的轻量级的ORM框架,常用于Android平台,但它并不是 Android 平台专用的ORM框架,它也可用于普通的Java环境中。 ORMLite除了支持Sqlite外还支持MySQL, Postgres, Microsoft SQL Server, H2, Derby, HSQLDB等数据库。支持JDBC连接,Spring以及Android平台。语法中广泛使用了注解(Annotation)。
必须的jar包:ORMLite的核心包是ormlite-core.jar,在普通的java环境中使用还需要添加ormlite-jdbc.jar,在Android环境中使用则需要ormlite-android.jar。
可选的jar包:(Log包)
Apache commons logging,,Log4j,Android Log 等
ORMLite启动后log代码会在classpath中依次查找android.util.Log、org.apache.commons.logging.LogFactory、org.apache.log4j.Logger等,如果找到前一个则不再继续查找。如果这几个类都不存在,ORMLite则使用自身内置的log代码记录日志。
本文以普通java环境中ORMLite的使用为例展示ORMLite的常用技巧和注意事项。ORMLite在Android环境中的使用与普通java环境中的使用基本一致,不同的是数据源以及配置的不同,而且Android环境中ormlite-android.jar提供了一些特定的工具类,使用起来更加方便,具体请参见官网:http://ormlite.com/。
入门示例
ORMLite的使用非常简单,先看一个入门代码示例:
@DatabaseTable(tableName = "accounts")
public class Account {
@DatabaseField(id = true)
private String name;
@DatabaseField(canBeNull = false)
private String password;
Account() {
// 必须有一个无参数构造函数
}
Account(String name, String pwd) {
this.name=name;
this.password=pwd;
}
}
// 数据库url
String databaseUrl = "jdbc:h2:mem:account";
// 创建一个数据库连接
ConnectionSource connectionSource =
new JdbcConnectionSource(databaseUrl);
// 创建DAO对象
Dao<Account,String> accountDao =
DaoManager.createDao(connectionSource, Account.class);
// 创建表(视需求而定)
TableUtils.createTable(connectionSource, Account.class);
// 创建实体对象
String name = "Jim Smith";
Account account = new Account(name, "_secret");
// 持久化到数据库
accountDao.create(account);
// 从数据库中查询
Account account2 = accountDao.queryForId(name);
System.out.println("Account: " + account2.getPassword());
// 关闭连接池
connectionSource.close();
ORMLite中注解的使用
ORMLite使用注解标明实体字段与数据库表字段的对应关系。注解既可使用ORMLite的注解,如 @DatabaseTable,@DatabaseField等;也可使用javax.persistence的注解,如@Entity、@Id、@Column等
比如:
@DatabaseTable(tableName = "accounts")
public class Account {
@DatabaseField(id = true)
private String name;
@DatabaseField(canBeNull = false)
private String password;
// …
}
等价于
@Entity(name = "accounts")
public class Account {
@Id
private String name;
@Column(nullable = false)
private String password;
// …
}
注意事项:
- 实体类必须有一个无参构造函数,有get/set方法
ORMLite连接池的使用
ORMLite对连接池做了一层封装,使用com.j256.ormlite.support.ConnectionSource接口表示。ConnectionSource接口的实现类有JdbcConnectionSource、JdbcPooledConnectionSource、DataSourceConnectionSource等,分别代表单个连接的连接池、jdbc可复用连接池、数据源连接池等。
示例如下:
final String url="jdbc:h2:mem:account";
// 单个连接的连接池
ConnectionSource jdbcconnectionSource =
new JdbcConnectionSource(url);
// 带缓存的连接池
JdbcPooledConnectionSource pooledconnectionSource =
new JdbcPooledConnectionSource(url);
// 数据源
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(url);
// 数据源连接池
ConnectionSource dsconnectionSource =
new DataSourceConnectionSource(dataSource, url);
注意事项:
- DataSource接口没有close方法,因而调用DataSourceConnectionSource的close方法其实什么也没做,DataSource需要我们手动关闭。
- JdbcConnectionSource不是线程安全的,不能用在多线程环境中
ORMLite中DAO的创建
Data Access Object (DAO) 可以使用DaoManager.createDao方法创建,也可以继承BaseDaoImpl类创建具有额外功能的自定义DAO。
示例如下:
//使用DaoManager创建Dao,泛型第一个参数为实体类,第二个参数为主键类
Dao<User, Integer> userDao =
DaoManager.createDao(connectionSource, User.class);
自定义DAO
/** 继承BaseDaoImpl实现自定义DAO类 */
public class UserDaoImpl extends BaseDaoImpl<User, Integer>{
public UserDaoImpl(ConnectionSource connectionSource)
throws SQLException {
super(connectionSource, User.class);
}
}
//在实体类上用注解标明自定义DAO的类文件
@DatabaseTable(daoClass = UserDaoImpl.class)
public class Account {
//…
}
注意事项:
- 创建DAO的开销比较大,尽量将DAO重复利用。DaoManager具有缓存功能,可以将创建的DAO缓存下来,避免重复创建,因此尽量使用DaoManager创建DAO
ORMLite使用DAO进行增删改查操作
ORMLite框架的DAO接口为用户提供了丰富的CRUD操作的重载方法。
CRUD示例:
//构造JavaBean
User u=new User(111,"ormlite_111");
//插入数据
userDao.create(u);
//更新
u1.setName("ormlite_111_updated");
userDao.update(u);
//查询
User uq = userDao.queryForId(111);
//删除
userDao.delete(u);
注意事项:查询、更新、删除操作要求实体类标明主键(id)
ORMLite的DAO接口还是可迭代的。可以通过Dao接口遍历一张数据表。示例如下:
for (Account account : accountDao) {
System.out.println(account.getName());
}
注意事项:Dao接口的循环迭代要求迭代完全部而不能中途返回(比如for循环中有return语句),否则迭代器不会被关闭从而造成资源泄露
也可以使用显式的迭代器,确保手动关闭迭代器资源,如下:
CloseableIterator<Account> iterator =
accountDao.closeableIterator();
try {
while (iterator.hasNext()) {
Account account = iterator.next();
System.out.println(account.getName());
}
} finally {
// close it at the end to close underlying SQL statement
iterator.close();
}
DAO Enabled 实体
ORMLite允许实体类自身具有类似Dao的功能,通过继承BaseDaoEnabled类实现。
示例:
@DatabaseTable(tableName = "t_test")
public class User extends BaseDaoEnabled<User, Object> {
@DatabaseField(id = true)
private int id;
@DatabaseField(canBeNull = false)
private String name;
// …
}
CRUD操作如下:
User u=new User();
u.setDao(userDao);
u.create();//insert
u.refresh();//query the new data
u.update();//update
u.delete();//delete
ORMLite中数据库与表的创建
ORMLite提供一些工具类用于创建和销毁Schema和table。
TableUtils类:
根据实体类直接创建表
TableUtils.createTable(connectionSource, Account.class);
根据config创建表
DatabaseFieldConfig dfc=new DatabaseFieldConfig();
// dfc.setColumnName("name");
List<DatabaseFieldConfig> fieldConfigs = Arrays.asList(dfc);
DatabaseTableConfig<Account> tableConfig =
new DatabaseTableConfig<Account>(Account.class, fieldConfigs);
// 创建表
TableUtils.createTable(connectionSource, tableConfig);
如果没有则创建
TableUtils.createTableIfNotExists(connectionSource, tableConfig);
删除表
TableUtils.dropTable(ConnectionSource, Class, boolean ignoreErrors);
这些创建、删除表的操作对测试非常方便,可以在测试类中创建表、删除表等
ORMLite对原生SQL支持
ORMLite定义的Dao功能并不保证覆盖所有的SQL操作,比如sum、count、avg等函数功能,因而ORMLite提供了对原生SQL的支持。
查询操作示例:
GenericRawResults<String[]> rawResults
=accountDao.queryRaw("select count(*) from t_test where id < 10");
还可以使用QueryBuilder构建prepareStatement查询:
QueryBuilder<Account, Integer> qb = accountDao.queryBuilder();
qb.where().gt("orderCount", 10);
GenericRawResults<String[]> results = accountDao.queryRaw(qb.prepareStatementString());
带参数的prepareStatement查询
QueryBuilder<Account, Integer> qb = accountDao.queryBuilder();
qb.where().lt("orderCount", new SelectArg());
GenericRawResults<String[]> results = accountDao.queryRaw(qb.prepareStatementString(), "10");
查询结果自动映射到对象
// 传入映射对象
GenericRawResults<User> rawResults =
userDao.queryRaw(
"select id,name from t_test order by id",
new RawRowMapper<User>() {
public User mapRow(String[] columnNames,
String[] resultColumns) {
return new User(Integer.parseInt(resultColumns[0]),resultColumns[1]); } });
// 取出映射结果
for (User u : rawResults) {
System.out.println(u);
}
rawResults.close();
注意事项:
- rawResults是可迭代的,迭代全部结果集后会自动关闭,否则结果集最后必须手动关闭;
- 使用prepareStatement查询时,如果查询字段中没有id字段,则QueryBuilder会自动添加上去,因此查询结果可能会比预想的多一列。
更新示例:(包括Insert,Update,Delete)
userDao.updateRaw("INSERT INTO t_test (id, name) VALUES(111,Lee)");
ORMLite使用QueryBuilder构建高级查询
使用ORMLite的QueryBuilder可以构建类似于原生SQL的自定义查询,而且使用更加方便。
示例1:(where条件查询)
QueryBuilder<Account, Object> queryBuilder =
accountDao.queryBuilder();
Where<Account, Object> where = queryBuilder.where();
where.eq(Account.NAME_FIELD_NAME, "foo");
where.and();
where.eq(Account.PASSWORD_FIELD_NAME, "_secret");
PreparedQuery<Account> preparedQuery = queryBuilder.prepare();
```
上述符合查询相当于如下SQL:
`SELECT * FROM account WHERE (name = 'foo' AND password = '_secret')`
示例2:复杂查询
```java
Where<Account, Object> where = queryBuilder.where();
where.or(
where.and(
where.eq(Account.NAME_FIELD_NAME, "foo"),
where.eq(Account.PASSWORD_FIELD_NAME, "_secret")),
where.and(
where.eq(Account.NAME_FIELD_NAME, "bar"),
where.eq(Account.PASSWORD_FIELD_NAME, "qwerty"))
);
<div class="se-preview-section-delimiter"></div>
相当于如下SQL:
SELECT * FROM account WHERE ((name = 'foo' AND password = '_secret') OR (name = 'bar' AND password = 'qwerty'))
ORMLite进行联合查询
ORMLite支持Join联合查询,示例如下
QueryBuilder<User, Object> userQb = userDao.queryBuilder();
userQb.where().ge("id", 10);
QueryBuilder<Account, Object> accountQb = accountDao.queryBuilder();
// join with the order query
List<Account> results = accountQb.join(userQb).query();
<div class="se-preview-section-delimiter"></div>
注意事项:ORMLite仅支持内连接与左连接,不支持右连接与全连接
ORMLite在Spring中的配置
ORMLite支持Spring容器中使用
下面的示例片段是Spring对ORMLite的url,数据源,connectionsource,Dao工厂的配置片段:(完整的配置文件见附录代码)
<!-- database url -->
<bean id="databaseUrl" class="java.lang.String">
<constructor-arg index="0" value="jdbc:sqlite:Q:/sqlite3/abc.db" />
</bean>
<!--使用dbcp数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.sqlite.JDBC" />
<property name="url" ref="databaseUrl" />
</bean>
<!-- ConnectionSource -->
<bean id="connectionSource" class="com.j256.ormlite.jdbc.DataSourceConnectionSource"
init-method="initialize">
<property name="databaseUrl" ref="databaseUrl" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- accountDao -->
<bean id="accountDao" class="com.j256.ormlite.spring.DaoFactory" factory-method="createDao">
<constructor-arg index="0" ref="connectionSource" />
<constructor-arg index="1" value="com.fiberhome.ormlite.spring.Account" />
</bean>
<div class="se-preview-section-delimiter"></div>
*注意事项:
1、所有类型ConnectionSource必须配置初始化方法initialize,否则抛异常
2、前文已提到DataSourceConnectionSource无法关闭数据源,因而数据源需要配置destroy-method=”close” 项来关闭数据源*
ORMLite事务支持与批量操作
Sqlite支持事务。使用ORMLite时可用通过ORMLite内置的事务管理器完成事务操作。
事务操作示例代码如下:
TransactionManager.callInTransaction(connectionSource,
new Callable<Void>() {
public Void call() throws Exception {
// insert our order
accountDao.create(account);
// now add the account to the order
order.setAccount(account);
// update our order object
orderDao.update(order);
// you could pass back an object here
return null;
}
});
<div class="se-preview-section-delimiter"></div>
ORMLite支持批量操作。对于大量的插入、修改等操作来说,调用批量操作接口可用提高数据库的性能。
示例代码如下:
accountDao.callBatchTasks(new Callable<Void>() {
public Void call() throws Exception {
for (Account account : accountsToInsert) {
accountDao.create(account);
}
}
});
*注意事项:
1、从源码层面看,事务与批量操作的实现十分相似,都是首先setAutoCommit(false),然后执行一系列操作,最后在连接上执行 commit()方法,不同的是如果发生异常,事务会回滚而批量操作不会回滚。
2、事物内部通过调用connectionSource的getReadWriteConnection() 方法获取一个数据库连接,然后在该数据库连接上进行事务操作。connectionSource内部通过一个ThreadLocal变量保证同一个线程获取到的数据库连接是同一个连接,我们也可以手动的获取连接配置提交事务,但最后要记得释放连接到连接池中。*
“`
注意事项:
- ORMLite仅支持内连接与左连接,不支持右连接与全连接
ORMLite在Spring中的配置
ORMLite支持Spring容器中使用
下面的示例片段是Spring对ORMLite的url,数据源,connectionsource,Dao工厂的配置片段:(完整的配置文件见附录代码)
“`xml
<!--使用dbcp数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.sqlite.JDBC" />
<property name="url" ref="databaseUrl" />
</bean>
<!-- ConnectionSource -->
<bean id="connectionSource" class="com.j256.ormlite.jdbc.DataSourceConnectionSource"
init-method="initialize">
<property name="databaseUrl" ref="databaseUrl" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- accountDao -->
<bean id="accountDao" class="com.j256.ormlite.spring.DaoFactory" factory-method="createDao">
<constructor-arg index="0" ref="connectionSource" />
<constructor-arg index="1" value="com.fiberhome.ormlite.spring.Account" />
</bean>
“`
注意事项:
- 所有类型ConnectionSource必须配置初始化方法initialize,否则抛异常
- 前文已提到DataSourceConnectionSource无法关闭数据源,因而数据源需要配置destroy-method=”close” 项来关闭数据源
ORMLite事务支持与批量操作
Sqlite支持事务。使用ORMLite时可用通过ORMLite内置的事务管理器完成事务操作。
事务操作示例代码如下:
“`java
TransactionManager.callInTransaction(connectionSource,
new Callable() {
public Void call() throws Exception {
// insert our order
accountDao.create(account);
// now add the account to the order
order.setAccount(account);
// update our order object
orderDao.update(order);
// you could pass back an object here
return null;
}
});
ORMLite支持批量操作。对于大量的插入、修改等操作来说,调用批量操作接口可用提高数据库的性能。
示例代码如下:
```java
accountDao.callBatchTasks(new Callable<Void>() {
public Void call() throws Exception {
for (Account account : accountsToInsert) {
accountDao.create(account);
}
}
});
注意事项:
- 从源码层面看,事务与批量操作的实现十分相似,都是首先setAutoCommit(false),然后执行一系列操作,最后在连接上执行 commit()方法,不同的是如果发生异常,事务会回滚而批量操作不会回滚。
- 事物内部通过调用connectionSource的getReadWriteConnection() 方法获取一个数据库连接,然后在该数据库连接上进行事务操作。connectionSource内部通过一个ThreadLocal变量保证同一个线程获取到的数据库连接是同一个连接,我们也可以手动的获取连接配置提交事务,但最后要记得释放连接到连接池中。