在做一个Android的项目,因为使用数据库频繁,实体字段也比较多,于是打算采用ORM框架,发现OrmLite还不错,于是下了下来,打算使用。
没想到还没正式开工,就遇到问题了。我现在的一个需求如下,
我有一个实体类如下,代表聊天消息,现在要做的是针对每一个当前用户(userId)对应一个朋友(friendId)都要创建一个表。需求比较蛋疼,我本来想的是直接在加两个字段就搞定的,但是我们老大说要分表。没办法只能分表。
public class ChatMessage{ public ChatMessage() { } private int _id; private int type; private String content; /*get and set...*/ }
在OrmLite里面创建表和Dao的基本用法如下:
DatabaseTableConfig<ChatMessage> config = DatabaseTableConfigUtil.fromClass(mHelper.getConnectionSource(), ChatMessage.class); TableUtils.createTableIfNotExists(mHelper.getConnectionSource(),config); dao = DaoManager.createDao(mHelper.getConnectionSource(), config);
这样我们就拿到了Dao对象,就可以进行数据操作了。但是这个方法的对我上面的需求并不管用,因为此方法拿到的数据库表名是固定的tableName="ChatMessage",我现在逍遥的表名肯定是不能固定的,他的格式是tableName="ChatMessage"+userId+friendId。即使在confi里面config.setTableName(tableName) 一样不管用。
查看了OrmLite的源码,发现在DaoManager里面,根据同样的DatabaseTableConfig和类名做了缓存,于是每次拿到的Dao都是同样的Dao
TableConfigConnectionSource tableKey = new TableConfigConnectionSource(connectionSource, tableConfig); // look up in the table map Dao<?, ?> dao = lookupDao(tableKey); if (dao != null) { @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } // now look it up in the class map Class<T> dataClass = tableConfig.getDataClass(); ClassConnectionSource classKey = new ClassConnectionSource(connectionSource, dataClass); dao = lookupDao(classKey); if (dao != null) { // if it is not in the table map but is in the class map, add it addDaoToTableMap(tableKey, dao); @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; }
同样的TableUtils.createTableIfNotExists一样进行了判断,使得你的相同的实体类不能创建多张表。
OrmLite这样做肯定是为了性能的优化和数据异步操作的安全性,但是这却妨碍了更加方便的使用了。于是研究下,稍微使了点偏招,来达到我上面的需求。
1、首先建个类,如下:
import java.sql.SQLException; import com.j256.ormlite.dao.BaseDaoImpl; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.DatabaseTableConfig; import com.roamer.bean.ChatMessage; public class ChatMessageDaoImpl extends BaseDaoImpl<ChatMessage, Integer>{ public ChatMessageDaoImpl(ConnectionSource connectionSource, DatabaseTableConfig<ChatMessage> tableConfig) throws SQLException { super(connectionSource, tableConfig); } }
实现BaseDaoImpl的原因是,查看源码,发现在DaoManager.createDao中实例化普通Modal,最后实际都是BaseDaoImpl类。
DatabaseTable databaseTable = tableConfig.getDataClass().getAnnotation(DatabaseTable.class); if (databaseTable == null || databaseTable.daoClass() == Void.class || databaseTable.daoClass() == BaseDaoImpl.class) { Dao<T, ?> daoTmp = BaseDaoImpl.createDao(connectionSource, tableConfig); dao = daoTmp; } else { Class<?> daoClass = databaseTable.daoClass(); Object[] arguments = new Object[] { connectionSource, tableConfig }; Constructor<?> constructor = findConstructor(daoClass, arguments); if (constructor == null) { throw new SQLException( "Could not find public constructor with ConnectionSource, DatabaseTableConfig parameters in class " + daoClass); } try { dao = (Dao<?, ?>) constructor.newInstance(arguments); } catch (Exception e) { throw SqlExceptionUtil.create("Could not call the constructor in class " + daoClass, e); } }
2、ChatMessageDaoImpl指定daoClass
@DatabaseTable(daoClass=ChatMessageDaoImpl.class) public class ChatMessage{ public ChatMessage() { } @DatabaseField(generatedId=true) private int _id; @DatabaseField private int type; @DatabaseField private String content; /*get and set*/ }
3、仿照DaoManager,实现一个不缓存的UnlimitDaoManager
package com.roamer.db; import java.lang.reflect.Constructor; import java.sql.SQLException; import com.j256.ormlite.dao.BaseDaoImpl; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.misc.SqlExceptionUtil; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.DatabaseTable; import com.j256.ormlite.table.DatabaseTableConfig; public class UnlimitDaoManager { public synchronized static <D extends Dao<T, ?>, T> D createDao(ConnectionSource connectionSource, DatabaseTableConfig<T> tableConfig) throws SQLException { if (connectionSource == null) { throw new IllegalArgumentException("connectionSource argument cannot be null"); } return doCreateDao(connectionSource, tableConfig); } private static Constructor<?> findConstructor(Class<?> daoClass, Object[] params) { for (Constructor<?> constructor : daoClass.getConstructors()) { Class<?>[] paramsTypes = constructor.getParameterTypes(); if (paramsTypes.length == params.length) { boolean match = true; for (int i = 0; i < paramsTypes.length; i++) { if (!paramsTypes[i].isAssignableFrom(params[i].getClass())) { match = false; break; } } if (match) { return constructor; } } } return null; } private static <D extends Dao<T, ?>, T> D doCreateDao(ConnectionSource connectionSource, DatabaseTableConfig<T> tableConfig) throws SQLException { Dao<?, ?> dao = null; // build the DAO using the table information DatabaseTable databaseTable = tableConfig.getDataClass().getAnnotation(DatabaseTable.class); if (databaseTable == null || databaseTable.daoClass() == Void.class || databaseTable.daoClass() == BaseDaoImpl.class) { return null; } else { Class<?> daoClass = databaseTable.daoClass(); Object[] arguments = new Object[] { connectionSource, tableConfig }; Constructor<?> constructor = findConstructor(daoClass, arguments); if (constructor == null) { throw new SQLException( "Could not find public constructor with ConnectionSource, DatabaseTableConfig parameters in class " + daoClass); } try { dao = (Dao<?, ?>) constructor.newInstance(arguments); } catch (Exception e) { throw SqlExceptionUtil.create("Could not call the constructor in class " + daoClass, e); } } @SuppressWarnings("unchecked") D castDao = (D) dao; return castDao; } }
4、因为上面没有使用DaoManager,所以为了性能和安全的考虑,我们还是要基本的实现以下缓存功能,下一个数据库操作的工具类,如下:
package com.roamer.dao; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import android.content.Context; import android.database.Cursor; import android.util.Log; import com.j256.ormlite.android.DatabaseTableConfigUtil; import com.j256.ormlite.android.apptools.OpenHelperManager; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.table.DatabaseTableConfig; import com.roamer.bean.ChatMessage; import com.roamer.db.SQLiteHelper; import com.roamer.db.UnlimitDaoManager; public class ChatMessageUtil { private static ChatMessageUtil instance; public static ChatMessageUtil getInstance(Context context) { if (instance == null) { instance = new ChatMessageUtil(context); } return instance; } private SQLiteHelper mHelper; private static final String PREFIX = "message_prefix"; public ChatMessageUtil(Context context) { mHelper = OpenHelperManager.getHelper(context, SQLiteHelper.class); } private Map<String, Dao<ChatMessage, Integer>> mDaoMap = new HashMap<String, Dao<ChatMessage, Integer>>(); private Dao<ChatMessage, Integer> getDao(String userId, String friendId) { String tableName = PREFIX + userId + friendId; if (mDaoMap.containsKey(tableName)) { return mDaoMap.get(tableName); } Dao<ChatMessage, Integer> dao = null; try { DatabaseTableConfig<ChatMessage> config = DatabaseTableConfigUtil.fromClass(mHelper.getConnectionSource(), ChatMessage.class); config.setTableName(tableName); createTableIfNotExist(tableName); dao = UnlimitDaoManager.createDao(mHelper.getConnectionSource(), config); } catch (SQLException e) { e.printStackTrace(); } if (dao != null) { mDaoMap.put(tableName, dao); } return dao; } private void createTableIfNotExist(String tableName) { if (isTableExist(tableName)) { return; } String sql = "CREATE TABLE " + tableName + " (content VARCHAR , _id INTEGER PRIMARY KEY AUTOINCREMENT , type INTEGER )"; mHelper.getWritableDatabase().execSQL(sql); Log.d("roamer", "isTableExist(tableName):" + isTableExist(tableName)); } private boolean isTableExist(String tableName) { boolean result = false; if (tableName == null) { return false; } Cursor cursor = null; try { String sql = "select count(*) as c from Sqlite_master where type =‘table‘ and name =‘" + tableName.trim() + "‘ "; cursor = mHelper.getReadableDatabase().rawQuery(sql, null); if (cursor.moveToNext()) { int count = cursor.getInt(0); if (count > 0) { result = true; } } } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null) { cursor.close(); } } return result; } public void addMessage(String userId, String friendId, ChatMessage message) { Dao<ChatMessage, Integer> dao = getDao(userId, friendId); try { dao.create(message); } catch (SQLException e) { e.printStackTrace(); } } public List<ChatMessage> getAllMessage(String userId, String friendId) { Dao<ChatMessage, Integer> dao = getDao(userId, friendId); try { return dao.queryForAll(); } catch (SQLException e) { e.printStackTrace(); } return null; } }
在这个里面,没有使用TableUtils来创建表,而是使用了原生SQL语句。
最后经测试,可以达到我拿蛋疼的需求。
写这个文章,是看到有人遇到和我相同的需求,不知道怎么解决,需求有点帮助。
OrmLite动态创建表,一个实体类创建多张表的的偏招,布布扣,bubuko.com