最近由于工作原因,经常需要保存用户数据,其中涉及创建表格、增删改查操作。虽然 SQLiteDatabase 提供 insert 、delete、update、query 方法,但每次都要小心翼翼传入参数,对于频繁操作数据数据比较容易出错,影响工作效率。现在我重新学习项目中数据库设计的方法,其中把每一张表字段写在一个bean,通过继承基础 BaseDao ,调用公共的数据库指令。 这样做的好处很明显:①逻辑清晰,方便以后数据的增添;②操作规范,不用每次打开数据库或者关闭数据库,避免忘记关闭数据库等误操作。
最近由于工作原因,经常需要保存用户数据,其中涉及创建表格、增删改查操作。虽然 SQLiteDatabase 提供 insert 、delete、update、query 方法,但每次都要小心翼翼传入参数,对于频繁操作数据数据比较容易出错,影响工作效率。现在我重新学习项目中数据库设计的方法,其中把每一张表字段写在一个bean,通过继承基础 BaseDao ,调用公共的数据库指令。 这样做的好处很明显:①逻辑清晰,方便以后数据的增添;②操作规范,不用每次打开数据库或者关闭数据库,避免忘记关闭数据库等误操作。
现在自己写了一个学生信息的 Demo ,SQL 操作示意图如下,有兴趣的朋友可以看看。
代码详解
1、布局文件相当简单,设置创建数据库、更新数据库和增删改查操作,代码如下所示:
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 android:orientation="vertical">
6
7 <Button
8 android:id="@+id/createDatabase"
9 android:layout_width="fill_parent"
10 android:layout_height="wrap_content"
11 android:background="@color/colorBackGround"
12 android:text="创建数据库" />
13
14 <Button
15 android:id="@+id/updateDatabase"
16 android:layout_width="fill_parent"
17 android:layout_height="wrap_content"
18 android:layout_marginTop="5dp"
19 android:background="@color/colorBackGround"
20 android:text="更新数据库" />
21
22 <Button
23 android:id="@+id/insert"
24 android:layout_width="fill_parent"
25 android:layout_height="wrap_content"
26 android:layout_marginTop="5dp"
27 android:background="@color/colorBackGround"
28 android:text="插入数据" />
29
30 <Button
31 android:id="@+id/update"
32 android:layout_width="fill_parent"
33 android:layout_height="wrap_content"
34 android:layout_marginTop="5dp"
35 android:background="@color/colorBackGround"
36 android:text="更新数据" />
37
38 <Button
39 android:id="@+id/query"
40 android:layout_width="fill_parent"
41 android:layout_height="wrap_content"
42 android:layout_marginTop="5dp"
43 android:background="@color/colorBackGround"
44 android:text="查询数据" />
45
46 <Button
47 android:id="@+id/delete"
48 android:layout_width="fill_parent"
49 android:layout_height="wrap_content"
50 android:layout_marginTop="5dp"
51 android:background="@color/colorBackGround"
52 android:text="删除数据" />
53 </LinearLayout>
布局很简单,页面如下所示
2、自定义一个继承 SQLiteOpenHelper 的类 SQLiteHelper,重写 onCreate(SQLiteDatabase sqLiteDatabase) 和 onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) 方法。其中当调用 SQLiteHelper 的 getReadableDatabase() 或者 getWritableDatabase() 时,如果没有创建数据库,则自动创建数据库;接着调用 onCreate(SQLiteDatabase sqLiteDatabase) 方法,一般在这里创建数据库表,当有版本更新时,走 onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) 方法,更新最新的数据库表。
1 /**
2 * Created by LCS on 2016/11/1.
3 */
4 public class SQLiteHelper extends SQLiteOpenHelper {
5
6 private static final String TAG = "LCS_SQLiteHelper";
7 private static final String db_name = "sql.db";//数据库名称
8 private static final SQLiteDatabase.CursorFactory factory = null;//暂时用不到
9 private static final int version = 1;//版本号,方便以后项目更新用户数据库
10 private static SQLiteHelper sqLiteHelper = null;//实例化 SQLiteHelper 对象
11
12 //创建班级通讯录
13 private static final String create_class_address = SQLInstruction.createClassAddress();
14 /**
15 * 构造函数
16 * @param context
17 */
18 public SQLiteHelper(Context context){
19 super(context, db_name, factory, version);
20 }
21
22 /**
23 * 获取实例
24 * @param context
25 * @return
26 */
27 public static SQLiteHelper getInstance(Context context){
28
29 if(sqLiteHelper == null){
30 sqLiteHelper = new SQLiteHelper(context);
31 }
32 return sqLiteHelper;
33 }
34
35 /**
36 * 第一次创建数据库,调用此方法
37 * @param sqLiteDatabase
38 */
39 @Override
40 public void onCreate(SQLiteDatabase sqLiteDatabase) {
41
42 //创建班级通讯录表
43 sqLiteDatabase.execSQL(create_class_address);
44 Log.d(TAG,"create db");
45 }
46
47 /**
48 * 更新数据库
49 * @param sqLiteDatabase
50 * @param oldVersion
51 * @param newVersion
52 */
53 @Override
54 public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
55
56 if(version > 1){
57 sqLiteDatabase.execSQL(create_class_address);
58 Log.d(TAG,"upgrade db");
59 }
60 }
61 }
3、先构建数据库连接管理器类 ConnectionProvider,这是真正执行数据库语句的位置。其中 execute(String sql) 可以执行数据库增加、删除和更新操作,query(String sql) 执行查找操作。被执行的的数据库指令为 Stiing 类型,我们只需要检查此语句正确性 ( 推荐使用 SQLite3 可视化工具 ),传入给对应的方法即可,是不是很方便?
1 /**
2 * Created by lcs on 2016/11/2.
3 * 连接管理器,数据库语句真正执行位置
4 */
5 public class ConnectionProvider {
6 private SQLiteDatabase db;
7 private SQLiteHelper sqLiteHelper;
8 private Context context;
9 private static ConnectionProvider provider;
10
11 public ConnectionProvider(Context context){
12 this.context = context;
13 /* //初始化
14 initProvider();*/
15 }
16 //初始化
17 private void initProvider() {
18 if(sqLiteHelper == null){
19 sqLiteHelper = SQLiteHelper.getInstance(context);
20 db = null;
21 }
22 if(db == null){
23 db = sqLiteHelper.getWritableDatabase();
24 }
25 }
26
27 /**
28 * 真正执行数据库操作
29 * @param sql
30 */
31 public synchronized void execute(String sql){
32 //打开数据库
33 openDB();
34 db.execSQL(sql);
35 //关闭数据库
36 closeDB(db);
37 }
38
39 /**
40 * 真正执行数据库操作
41 * @param sql
42 */
43 public synchronized Cursor query(String sql){
44 //打开数据库
45 openDB();
46 return db.rawQuery(sql,null);
47 }
48
49 /**
50 * 打开数据库
51 */
52 private void openDB() {
53 try {
54 initProvider();
55 }catch (Exception e){
56 e.printStackTrace();
57 }
58 }
59
60 /**
61 * 关闭数据库
62 * @param db
63 */
64 private void closeDB(SQLiteDatabase db) {
65 try {
66 sqLiteHelper = null;
67 db.releaseReference();
68 }catch (Exception e){
69 e.printStackTrace();
70 }
71 }
72
73 /**
74 * 获取 ConnectionProvider 实例
75 * @return
76 */
77 public static ConnectionProvider getInstance(Context context){
78 if(provider == null){
79 provider = new ConnectionProvider(context);
80 }
81 return provider;
82 }
83 }
4、构建基础 Dao。BaseDao 很简单,但很重要。当以后有操作数据库需求,只需要定义 某某 Dao 继承 BaseDao,即可调用公共的数据库操作。
1 /**
2 * 基础 Dao
3 * Created by lcs on 2016/11/2.
4 */
5 public abstract class BaseDao <T>{
6 private Context context;
7 private ConnectionProvider provider;
8
9 public BaseDao(Context context){
10 this.context = context;
11 this.provider = ConnectionProvider.getInstance(context);
12 }
13
14 /**
15 * 执行 SQL 语句
16 */
17 public synchronized void execute(String sql){
18 provider.execute(sql);
19 }
20
21 /**
22 * 执行 SQL 语句
23 */
24 public synchronized Cursor query(String sql){
25 return provider.query(sql);
26 }
27
28 /**
29 * 是否有结果集
30 * @param c
31 * @return
32 */
33 protected synchronized boolean hasResult(Cursor c){
34 boolean has=false;
35 if(c.moveToFirst()&&c.getCount()>0){
36 has=true;
37 }
38 return has;
39 }
40
41 }
5、创建学生信息 bean 。其中 StudentInfoBean 为需要存储进数据库的字段信息
1 /**
2 * Created by user on 2016/11/2.
3 * 学生信息
4 */
5 public class StudentInfoBean {
6 private String name = "";//姓名
7 private String id = "";//学号
8 private String age = "";//年龄
9 private String tall = "";//身高
10
11 public String getName() {
12 return name;
13 }
14
15 public void setName(String name) {
16 this.name = name;
17 }
18
19 public String getId() {
20 return id;
21 }
22
23 public void setId(String id) {
24 this.id = id;
25 }
26
27 public String getAge() {
28 return age;
29 }
30
31 public void setAge(String age) {
32 this.age = age;
33 }
34
35 public String getTall() {
36 return tall;
37 }
38
39 public void setTall(String tall) {
40 this.tall = tall;
41 }
42 }
6、创建学生 Dao。其中 StudentDao 继承 BaseDao,在通过学号 id 查找和删除学生信息方法中,其实也可以传入其他学生信息进行查找,例如学生 name 、age 等。还有通过 id 可能查到多条记录,规范写法应该写成返回 StudentInfoBean 的 List 。
1 /**
2 * Created by lcs on 2016/11/2.
3 * 学生信息的 Dao
4 */
5 public class StudentDao extends BaseDao {
6
7 public StudentDao(Context context) {
8 super(context);
9 }
10
11 /**
12 * 增加一条学生记录
13 * @param studentInfoBean
14 * @return
15 */
16 public boolean addStudent(StudentInfoBean studentInfoBean){
17 boolean flag = false;
18 if(studentInfoBean != null){
19 flag = true;
20 execute(SQLInstruction.addStudent(studentInfoBean));
21 return flag;
22 }else {
23 return flag;
24 }
25 }
26
27 /**
28 * 通过学号 id 删除学生信息
29 * @param id
30 * @return
31 */
32 public boolean deleteStudentById(String id){
33 boolean flag = false;
34 if(id != "" && !id.isEmpty()){
35 flag = true;
36 execute(SQLInstruction.deleteStudentById(id));
37 return flag;
38 }else {
39 return flag;
40 }
41 }
42
43 /**
44 * 更新指定列学生信息
45 * @param column
46 * @param old_value
47 * @param new_value
48 * @return
49 */
50 public boolean updateStudent(String column ,String old_value,String new_value){
51 boolean flag = false;
52 if(column != "" && !column.isEmpty()){
53 execute(SQLInstruction.updateStudent(column,old_value,new_value));
54 flag = true;
55 return flag;
56 }else {
57 return flag;
58 }
59 }
60
61 /**
62 * 通过学号 id 查询学生信息
63 * @param id
64 * @return
65 */
66 public StudentInfoBean queryStudent(String id){
67 StudentInfoBean studentInfoBean = new StudentInfoBean();
68 if(id != "" && !id.isEmpty()){
69 Cursor cursor = query(SQLInstruction.queryStudentById(id));
70 if(hasResult(cursor)){
71 studentInfoBean.setName(cursor.getString(cursor.getColumnIndex("NAME")));
72 studentInfoBean.setAge(cursor.getString(cursor.getColumnIndex("AGE")));
73 studentInfoBean.setTall(cursor.getString(cursor.getColumnIndex("TALL")));
74 cursor.moveToNext();
75 }
76 cursor.close();
77 }
78 return studentInfoBean;
79 }
80 }
7、看到现在,不知道你会不会觉得很奇怪,为什么还没有涉及真正数据库操作语句?那是因为我们把所有数据库语句放在一个类中 SQLInstruction。为方便调用,将每一个数据库操作方法设置为 static。看到这里,你是否发现如此设计数据库操作好处呢?
1 /**
2 * Created by user on 2016/11/1.
3 * 数据库语句
4 */
5 public class SQLInstruction {
6
7 /**
8 * 创建班级通讯录表
9 *
10 * @return
11 */
12 public static String createClassAddress() {
13 StringBuffer sql = new StringBuffer();
14 sql.append("create table IF NOT EXISTS CLASS_ADDRESS("
15 + "NAME String," + "ID int," +"AGE int,"
16 + "TALL int," + "CLASS_NAME String" + " )");
17 return sql.toString();
18 }
19
20 /**
21 * 在数据库添加一条学生记录
22 */
23 public static String addStudent(StudentInfoBean student){
24 StringBuffer sql = new StringBuffer("insert into CLASS_ADDRESS(")
25 .append("NAME,")
26 .append("ID,")
27 .append("AGE,")
28 .append("TALL")
29 .append(") VALUES(");
30 sql.append("'").append(student.getName()).append("',");
31 sql.append("'").append(student.getId()).append("',");
32 sql.append("'").append(student.getAge()).append("',");
33 sql.append("'").append(student.getTall()).append("')");
34 return sql.toString();
35 }
36
37 /**
38 * 通过学号 id 删除学生信息
39 * @param id
40 * @return
41 */
42 public static String deleteStudentById(String id){
43 StringBuffer sql = new StringBuffer();
44 sql.append("delete from CLASS_ADDRESS where ID = '");
45 sql.append(id).append("'");
46 return sql.toString();
47 }
48
49 /**
50 * 更新指定列学生信息
51 * @param column
52 * @param old_value
53 * @param new_value
54 * @return
55 */
56 public static String updateStudent(String column ,String old_value,String new_value){
57 StringBuffer sql = new StringBuffer();
58 sql.append("update CLASS_ADDRESS set ");
59 sql.append(column).append(" = ");
60 sql.append("'").append(new_value).append("'");
61 sql.append(" where ").append(column).append(" = ");
62 sql.append("'").append(old_value).append("'");
63 return sql.toString();
64 }
65
66 /**
67 * 通过学号 id 查询学生信息
68 * @param id
69 * @return
70 */
71 public static String queryStudentById(String id){
72 StringBuffer sql = new StringBuffer();
73 sql.append("select NAME,ID,AGE,TALL ");
74 sql.append("from CLASS_ADDRESS where ID = '");
75 sql.append(id).append("'");
76 return sql.toString();
77 }
78
79 }
8、进入最后一步操作,在 Activity 进行数据库操作。
1 @Override
2 public void onClick(View view) {
3 StudentDao studentDao = null;
4 switch (view.getId()){
5 case R.id.createDatabase://创建数据库
6 SQLiteHelper sqLiteHelper = new SQLiteHelper(SQLiteActivity.this);
7 SQLiteDatabase database_create = sqLiteHelper.getReadableDatabase();
8 database_create.close();
9 break;
10
11 case R.id.updateDatabase://更新数据库
12 SQLiteHelper sqLiteHelper_update = new SQLiteHelper(SQLiteActivity.this);
13 SQLiteDatabase database_update = sqLiteHelper_update.getWritableDatabase();
14 database_update.close();
15 break;
16
17 case R.id.insert://插入两条学生记录
18 if(studentDao == null){
19 studentDao = new StudentDao(SQLiteActivity.this);
20 }
21 boolean insert_result1 = studentDao.addStudent(studentInfoBean1);
22 boolean insert_result2 = studentDao.addStudent(studentInfoBean1);
23 Log.d(TAG,String.valueOf(insert_result1));
24 Log.d(TAG,String.valueOf(insert_result2));
25 break;
26
27 case R.id.delete://通过学号 id 删除学生信息
28 if(studentDao == null){
29 studentDao = new StudentDao(SQLiteActivity.this);
30 }
31 boolean delete_result = studentDao.deleteStudentById("1000001");
32 Log.d(TAG,String.valueOf(delete_result));
33 break;
34
35 case R.id.update://更新指定列学生信息
36 if(studentDao == null){
37 studentDao = new StudentDao(SQLiteActivity.this);
38 }
39 boolean update_result = studentDao.updateStudent("NAME", "小明", "飞天");
40 Log.d(TAG,String.valueOf(update_result));
41 break;
42
43 case R.id.query://通过学号 id 查询学生信息
44 if(studentDao == null){
45 studentDao = new StudentDao(SQLiteActivity.this);
46 }
47 StudentInfoBean studentInfoBean = studentDao.queryStudent("1000001");
48 String age = studentInfoBean.getAge();
49 String name = studentInfoBean.getName();
50 String tall = studentInfoBean.getTall();
51 Log.d(TAG,"age= " + age + "\nname = " + name + "\ntall= " + tall);
52 break;
53 }
54 }
现在重新看 SQL 操作示意图,应该会觉得好理解些吧!