Java利用映射机制来动态创建SQL语句。
原理
Java有映射机制,可以根据Class来获取其属性的名称,类型以及值。
例如:
Field[] fs = this.getClass().getDeclaredFields();
for(int index = 0; index < fs.length; ++index){
Field f = fs[index];
f.setAccessible(true);
String name = f.getName();
String type = f.getType().toString();
Object value = f.get(this);
}
注意:继承的属性获取不到。
Sqlite语法有一定的规律。
(1)建表:
CREATE TABLE database_name.table_name(
column1 datatype PRIMARY KEY(one or more columns),
column2 datatype,
column3 datatype,
.....
columnN datatype,
);
(2)插入:
INSERT INTO TABLE_NAME VALUES (value1,value2,value3,...valueN);
(3)更新:
UPDATE table_name SET column1 = value1, column2 = value2...., columnN = valueN WHERE [condition];
(4)查询:
SELECT * FROM table_name WHERE [condition];
(5)删除:
DELETE FROM table_name WHERE [condition];
结合
将两者结合在一起,即可以实现在Java Class中自动管理Sqlite数据库,包括建表,增删改查等,具体思路参考下文。
思路
一个Class管理一个表,彼此之间实现解耦,但是由于语法基本一致,可以直接在一个基类中定义好大部分的SQL语句模板,而在运行时利用多态,获取到子类的属性,并添加到SQL语句模板中,从而给Sqlite底层调用。
考虑到,还需要定义一些索引和约束(主外键等),这些只能在子类中进行自定义配置,然后基类进行获取。
反过来,从Sqlite获取到值后,还需要一一反映射到子类的属性中。
实现
实现基类
定义基类
public abstract class BaseDto {
public Integer id;
public abstract String tableName();
protected abstract HashMap<String, String> fieldConstraints();
protected abstract String tableConstraint();
//后续方法见下文
}
- 由于每个表都需要id,所以直接在基类中定义好,子类继承即可;
- 表名需要子类配置,实现为虚函数;
- fieldConstraints是用于表属性的额外配置,例如,标题是唯一的,fieldConstraints.put(“title”, “UNIQUE”);
- tableConstraint是用于表的约束,例如,外键,”foreign key(vehicle) references vehicle(id)”;
建表SQL
public String tableString(){
String tableStr = "CREATE TABLE IF NOT EXISTS " + this.tableName() + "(id INTEGER PRIMARY KEY, ";
try{
HashMap<String, String> fieldConstraints = fieldConstraints();
String tableConstraint = tableConstraint();
Field[] fs = this.getClass().getDeclaredFields();
for(int index = 0; index < fs.length; ++index){
Field f = fs[index];
f.setAccessible(true);
tableStr += f.getName() + " ";
String type = f.getType().toString();
if(type.endsWith("String"))
tableStr += "TEXT ";
else if(type.endsWith("int") || type.endsWith("Integer") ||
type.endsWith("boolean") || type.endsWith("Boolean"))
tableStr += "INTEGER ";
else if(type.endsWith("float") || type.endsWith("Float"))
tableStr += "REAL ";
else if(type.endsWith("double") || type.endsWith("Double"))
tableStr += "REAL ";
else if(type.endsWith("Date"))
tableStr += "DATETIME ";
else
throw new Exception("tableString format tranfer error..");
if(null != fieldConstraints && fieldConstraints.containsKey(f.getName()))
tableStr += fieldConstraints.get(f.getName());
if(index < fs.length - 1)
tableStr += ", ";
else if(null != tableConstraint && !tableConstraint.isEmpty())
tableStr += ", " + tableConstraint;
}
tableStr += " );";
}catch(Exception ex){
ex.printStackTrace();
}
return tableStr;
}
这里,需要注意的是,sqlite与Java支持的数据类型是不同的,需要进行一下类型转换。
插入SQL
public String insertString(){
String insertStr = "INSERT OR IGNORE INTO " + this.tableName() + " VALUES (" + id + ", ";
try{
Field[] fs = this.getClass().getDeclaredFields();
for(int index = 0; index < fs.length; ++index){
Field f = fs[index];
f.setAccessible(true);
String type = f.getType().toString();
if(type.endsWith("String"))
insertStr += "'" + f.get(this) + "'";
else if(type.endsWith("int"))
insertStr += f.getInt(this);
else if(type.endsWith("boolean"))
insertStr += f.getBoolean(this) ? 1 : 0;
else if(type.endsWith("float"))
insertStr += f.getFloat(this);
else if(type.endsWith("double"))
insertStr += f.getDouble(this);
else if(type.endsWith("Integer") || type.endsWith("Float") || type.endsWith("Double") || type.endsWith("Boolean"))
insertStr += f.get(this);
else if(type.endsWith("Date"))
insertStr += "'" + TimeUtil.dateToString((Date)f.get(this)) + "'";
else
throw new Exception("insertStr format tranfer error..");
if(index < fs.length - 1)
insertStr += ", ";
}
insertStr += ");";
}catch(Exception ex){
ex.printStackTrace();
}
return insertStr;
}
更新SQL
public String updateString(){
String updateStr = "UPDATE " + this.tableName() + " SET id = " + id + ", ";
try{
Field[] fs = this.getClass().getDeclaredFields();
for(int index = 0; index < fs.length; ++index){
Field f = fs[index];
f.setAccessible(true);
updateStr += f.getName() + " = ";
String type = f.getType().toString();
if(type.endsWith("String"))
updateStr += "'" + f.get(this) + "'";
else if(type.endsWith("int"))
updateStr += f.getInt(this);
else if(type.endsWith("boolean"))
updateStr += f.getBoolean(this) ? 1 : 0;
else if(type.endsWith("float"))
updateStr += f.getFloat(this);
else if(type.endsWith("double"))
updateStr += f.getDouble(this);
else if(type.endsWith("Integer") || type.endsWith("Float") || type.endsWith("Double") || type.endsWith("Boolean"))
updateStr += f.get(this);
else if(type.endsWith("Date"))
updateStr += "'" + TimeUtil.dateToString((Date)f.get(this)) + "'";
else
throw new Exception("updateStr format tranfer error..");
if(index < fs.length - 1)
updateStr += ", ";
}
updateStr += " WHERE id = " + id + ";";
}catch(Exception ex){
ex.printStackTrace();
}
return updateStr;
}
删除SQL
public String deleteString(){
String deleteString = "DELETE FROM " + this.tableName() + " WHERE id = " + id + ";";
return deleteString;
}
从Sqlite的ResultSet取值反映射到属性中
public void fromResultSet(ResultSet rs){
try{
id = (Integer) rs.getObject("id");
Field[] fs = this.getClass().getDeclaredFields();
for(int index = 0; index < fs.length; ++index){
Field f = fs[index];
f.setAccessible(true);
String type = f.getType().toString();
if(type.endsWith("String"))
f.set(this, rs.getString(f.getName()));
else if(type.endsWith("int"))
f.set(this, rs.getInt(f.getName()));
else if(type.endsWith("boolean"))
f.set(this, rs.getBoolean(f.getName()));
else if(type.endsWith("float"))
f.set(this, rs.getFloat(f.getName()));
else if(type.endsWith("double"))
f.set(this, rs.getDouble(f.getName()));
else if(type.endsWith("Integer") || type.endsWith("Float") || type.endsWith("Double") || type.endsWith("Boolean"))
f.set(this, rs.getObject(f.getName()));
else if(type.endsWith("Date")){
f.set(this, TimeUtil.stringToDate(rs.getString(f.getName())));
}
else
throw new Exception("fromResultSet format tranfer error..");
}
}catch(Exception ex){
ex.printStackTrace();
}
}
定义子类
子类与数据库表一一对应,例如:
1 | public class SSDto extends BaseDto{ |
定义DAO
DAO层在Database和Dto之间搭建桥梁,并封装一层,给外部调用。Sqlite连接参考Java-Sqlite连接。
例如:
private void create(BaseDto dto){
DBManager.manager().executeSQL(dto.tableString());
}
public void add(BaseDto dto){
DBManager.manager().executeSQL(dto.insertString());
}
public void update(BaseDto dto){
DBManager.manager().executeSQL(dto.updateString());
}
public void delete(BaseDto dto){
DBManager.manager().executeSQL(dto.deleteString());
}
public BaseDto query(BaseDto dto, String sql){
ArrayList<BaseDto> dtos = queryAll(dto, sql);
if(!dtos.isEmpty())
return dtos.get(0);
return null;
}
运行
public void main(String[] s){
Dao.dao().create(new SSDto());
}
用Sqlite管理软件打开db文件,可以看到表已经创建成功了。增删改查这里就不一一列举了。