很多Android应用都需要用到本地SQLite数据库,然而Android对于数据库操作的原生API使用起来略显复杂。ormlite是应用于Android数据库的一个很牛的开源项目,它的亮点在于可以直接写入数据结构,进行对象的增删改查操作。更进一步,如果对ormlite略微进行封装,使用起来将更加方便。本文只介绍对于ormlite的封装,不介绍ormlite的基本用法,关于这个项目的用法,网上有很多介绍的帖子和博客,麻烦读者自行学习啦~
整体结构设计思路如下:
DatabaseHelper类管理数据包的创建和销毁,使用单例模式。在其内部维护一个HashMap,可以通过Bean类找到对应的DAO类。
BaseBean类是所有Bean类的基类,它是抽象的空的类,只起标识的作用。其每一个子类对应一张数据表。
接下来是DAO类。BaseDAO类主要完成两个工作:1、建立DAO子类与对应的Bean类的联系;2、提供基本的增删改查的方法,这些方法与数据表无关。
闲话不多扯,下面上代码,首先是DatabaseHelper类,这个与网上很多栗子的设计思路相似。只不过增加了一个对于用户的判断,有很多用到数据库的App都是区分登录用户的,并且在用户登录之后将userId,用户名一类的信息存入到SharedPreference中,这里通过userId拿到数据库的db文件。
public class DatabaseHelper extends OrmLiteSqliteOpenHelper
{
private static DatabaseHelper instance;
private static String mCurUserId = "";
private Map<String, Dao> daos = new HashMap<String, Dao>();
private DatabaseHelper(Context context, String databaseName, int databaseVersion)
{
super(context, databaseName, null, databaseVersion);
}
public static synchronized DatabaseHelper getHelper(Context context)
{
String userid = /*从SharedPreference中读到的userId*/;
if (instance == null || !userid.equals(mCurUserId) )
{
synchronized (DatabaseHelper.class)
{
if (instance == null || !userid.equals(mCurUserId) )
{
instance = new DatabaseHelper(context,"YOUR_APP_NAME"+userid+".db", AppConstants.DATABASE_VERSION);
mCurUserId = userid;
}
}
}
return instance;
}
public synchronized Dao getDao(Class cls) throws SQLException
{
Dao dao = null;
String className = cls.getSimpleName();
if (daos.containsKey(className))
{
dao = daos.get(className);
}
if (dao == null)
{
dao = super.getDao(cls);
daos.put(className, dao);
}
return dao;
}
/**
* 释放资源
*/
@Override
public void close()
{
super.close();
for (String key : daos.keySet())
{
Dao dao = daos.get(key);
dao = null;
}
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource)
{
try
{
TableUtils.createTableIfNotExists(connectionSource, xxxxBean.class);
TableUtils.createTableIfNotExists(connectionSource, xxxxxxxxBean.class);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource,
int oldVersion, int newVersion)
{
try
{
TableUtils.dropTable(connectionSource, xxxxBean.class, true);
TableUtils.dropTable(connectionSource, xxxxxxxxBean.class, true);
onCreate(sqLiteDatabase,connectionSource);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
}
这个类里的onCreate()和onUpgrade()方法不解释了,getHelper()方法用于获取实例,这里会判断如果用户id发生了改变(比如换小号登录),会读取或者创建新的db文件(取决于之前这个db文件是否存在)。类中daos这个变量就是保存Bean类和对应DAO类之间关系的HashMap,getDao()是通过Key获取Value的方法,daos这个变量会在close()方法中清理掉。
前面说过,BaseBean类除了标识作用之外,没什么实际价值。
public abstract class BaseBean
{
}
接下来是BaseDAO类,绝大多数代码都是基本的增删改查方法,所以重点看一下构造函数。
public abstract class BaseDAO<T extends BaseBean>
{
protected Context context;
protected Dao<T,Integer> daoOpe;
protected DatabaseHelper helper;
/**
* 注意啦,由于数据库是区分用户的,因此应当在登录之后再调用子类的构造方法
* @param context
*/
public BaseDAO(Context context)
{
this.context = context;
helper = DatabaseHelper.getHelper(context);
ParameterizedType pt = (ParameterizedType)getClass().getGenericSuperclass();
Class<T> cls = (Class<T>) pt.getActualTypeArguments()[0];
try
{
daoOpe = helper.getDao(cls);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public void add(T bean)
{
try
{
daoOpe.create(bean);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public void addIfNotExists(T bean)
{
try
{
daoOpe.createIfNotExists(bean);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public void delete(T bean)
{
try
{
daoOpe.delete(bean);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public void delete(Collection<T> beans)
{
try
{
daoOpe.delete(beans);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public void clearTable()
{
ParameterizedType pt = (ParameterizedType)getClass().getGenericSuperclass();
Class<T> cls = (Class<T>) pt.getActualTypeArguments()[0];
try
{
TableUtils.clearTable(helper.getConnectionSource(), cls);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
public List<T> queryAll()
{
try
{
return daoOpe.queryForAll();
}
catch (SQLException e)
{
e.printStackTrace();
return null;
}
}
public List<T> queryAllOrder(String orderby, boolean asec)
{
try
{
QueryBuilder<T,Integer> qb = daoOpe.queryBuilder();
qb.orderBy(orderby, asec);
return daoOpe.query(qb.prepare());
}
catch (SQLException e)
{
e.printStackTrace();
return null;
}
}
/**
* 通常用于查找特定的一条记录
* @param key
* @param value
* @return
*/
public List<T> queryWithConditon(String key, Object value)
{
try
{
return daoOpe.queryForEq(key, value);
}
catch (SQLException e)
{
e.printStackTrace();
return null;
}
}
public List<T> queryWithConditionOrder(String key, Object value, String orderby, boolean asec)
{
try
{
QueryBuilder<T,Integer> qb = daoOpe.queryBuilder();
qb.where().eq(key,value);
qb.orderBy(orderby,asec);
return daoOpe.query(qb.prepare());
}
catch (SQLException e)
{
e.printStackTrace();
return null;
}
}
/**
* 常用复合条件查询 不能查询不等式
* @param keys
* @param values
* @param orderby
* @param limit
* @param offset
* @param asec
* @return
*/
public List<T> queryWithConditions(String[] keys,Object[] values,
String orderby ,Long limit,Long offset, boolean asec)
{
if(keys.length != values.length)
{
throw new IllegalArgumentException("number of keys MUST BE SAME with number of values");
}
try
{
QueryBuilder<T,Integer> qb = daoOpe.queryBuilder();
for(int i=0;i<keys.length;i++)
{
qb.where().eq(keys[i],values[i]);
}
if(limit != null && limit.longValue()!=0)
{
qb.limit(limit );
}
if(orderby!=null)
{
qb.orderBy(orderby,asec);
}
if(offset!=null && offset.longValue()!=0)
{
qb.offset( offset );
}
return daoOpe.query(qb.prepare());
}
catch (SQLException e)
{
e.printStackTrace();
return null;
}
}
public void update(T bean)
{
try
{
daoOpe.update(bean);
}
catch (SQLException e)
{
e.printStackTrace();
}
}
}
在构造函数中,首先获取泛型类T(即Bean类的子类)的class对象,然后调用DatabaseHelper类中的getDao()方法,获得对应的DAO类对象,赋给daoOpe变量。后面定义的增删改查操作都是利用daoOpe对泛型类T进行操作的。
在使用时BaseBean的子类只负责定义数据表,BaseDAO类的子类只负责关联Bean类和添加专用于数据表的操作。
/**
* Créé par liusiqian 15/12/4.
*/
@DatabaseTable(tableName = "student_info")
public class StudentBean extends BaseBean
{
@DatabaseField(generatedId = true)
private int _id;
@DatabaseField(canBeNull = false, defaultValue = "小明")
private String name; //姓名
@DatabaseField()
private int age; //年龄
@DatabaseField()
private String stuID; //ID
public StudentBean()
{
}
public StudentBean(String name, int age, String stuID)
{
this.name = name;
this.age = age;
this.stuID = stuID;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getStuID()
{
return stuID;
}
public void setStuID(String stuID)
{
this.stuID = stuID;
}
}
/**
* Créé par liusiqian 15/12/4.
*/
public class StudentDAO extends BaseDAO<StudentBean>
{
public StudentDAO(Context context)
{
super(context);
}
public boolean addSingleStudent(String stuId, String name, int age)
{
StudentBean bean = querySingleStudent(stuId);
if (null == bean)
{
add(new StudentBean(name, age, stuId));
return true;
}
else
{
bean.setName(name);
bean.setAge(age);
update(bean);
return false;
}
}
public boolean deleteSingleStudent(String stuId)
{
StudentBean bean = querySingleStudent(stuId);
if (null != bean)
{
delete(bean);
return true;
}
return false;
}
public StudentBean querySingleStudent(String stuId)
{
List<StudentBean> students = queryWithConditon("stuId", stuId);
if (students == null || students.size() == 0)
{
return null; //not found
}
else if (students.size() == 1)
{
return students.get(0);
}
else
{
throw new IllegalStateException("存在相同stuId的记录");
}
}
public void updateStudentAge(String stuId, int age)
{
StudentBean bean = querySingleStudent(stuId);
if (null != bean)
{
bean.setAge(age);
update(bean);
}
}
}
在使用的时候,直接调用各个DAO类的方法即可,是不是灰常方便~~
StudentDAO dao = new StudentDAO(this);
dao.addSingleStudent("144","张三",25);
dao.addSingleStudent("133","李四",23);
dao.deleteSingleStudent("133");