android学习笔记----SQLite数据库

xiaoxiao2021-02-28  47

 

 

目录

用SQLite语句执行:

利用SQLiteDatabase中自带的增删改查操作:

SQLite数据库的事务介绍:


目录一二的增删改查源码地址:https://github.com/liuchenyang0515/CreateDB3

目录三事务介绍的源码地址:https://github.com/liuchenyang0515/BankTransfer

 

官方更推荐Room而不是SQLite:https://developer.android.google.cn/training/data-storage/room

 

用SQLite语句执行:

首先看到界面:

​​​​

代码如下:

MainActivity.java

import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.EditText; import android.widget.Toast; import com.example.createdb2.dao.ContactInfoDao; public class MainActivity extends AppCompatActivity { private EditText et_name; private EditText et_phone; private ContactInfoDao dao; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1.找到需要用到的控件 et_name = (EditText) findViewById(R.id.et_name); et_phone = (EditText) findViewById(R.id.et_phone); // 2.new一个Dao出来 dao = new ContactInfoDao(this, "mydb.db", null, 1); } /** * 添加一条联系人信息 * * @param view */ public void add(View view) { // 做具体的添加操作 String name = et_name.getText().toString().trim(); String phone = et_phone.getText().toString().trim(); if (TextUtils.isEmpty(name) || TextUtils.isEmpty(phone)) { Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show(); return; } else { dao.add(name, phone); Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show(); } } /** * 删除一条联系人信息 * * @param view */ public void delete(View view) { String name = et_name.getText().toString().trim(); if (TextUtils.isEmpty(name)) { Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show(); return; } else { dao.delete(name); Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show(); } } /** * 修改联系人号码 * * @param view */ public void update(View view) { String name = et_name.getText().toString().trim(); String phone = et_phone.getText().toString().trim(); if (TextUtils.isEmpty(name) || TextUtils.isEmpty(phone)) { Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show(); return; } else { dao.update(phone, name); Toast.makeText(this, "修改成功", Toast.LENGTH_SHORT).show(); } } /** * 查询联系人号码 * * @param view */ public void query(View view) { String name = et_name.getText().toString().trim(); if (TextUtils.isEmpty(name)) { Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show(); return; } else { String phone = dao.query(name); if (phone != null) { Toast.makeText(this, "查询到的号码为:" + phone, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "无此联系人信息", Toast.LENGTH_SHORT).show(); } } } }

ContactInfoDao.java

import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.example.createdb2.MyDBOpenHelper; public class ContactInfoDao { private final MyDBOpenHelper helper; public ContactInfoDao(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { helper = new MyDBOpenHelper(context, name, factory, version); } /** * create table aa(id integer primary key autoincrement, name char(20), phone varchar(20)); * create table temp as select id, name from aa; // * temp表没有了PRIMARY KEY AUTOINCREMENT,查看建表语句CREATE TABLE "temp"(id INT,NAME TEXT); * integer变成了int * char变成text * 新表中没有旧表中的primary key,Extra,auto_increment等属性,需要自己手动加,具体参看后面的修改表即字段属性. * * @param name 联系人姓名 * @param phone 联系人电话 */ public void add(String name, String phone) { SQLiteDatabase db = helper.getWritableDatabase(); // 如果数据库已存在就打开,否则创建一个新数据库 db.execSQL("insert into contactinfo (name, phone) values(?, ?)", new Object[]{name, phone}); // 记得关闭数据库,释放资源 db.close(); } /** * 删除一条记录 * * @param name 联系人姓名 */ public void delete(String name) { SQLiteDatabase db = helper.getWritableDatabase(); db.execSQL("delete from contactinfo where name = ?", new Object[]{name}); // 记得关闭数据库,释放资源 db.close(); } /** * 更新一条记录 * * @param name 联系人姓名 * @param phone 联系人电话 */ public void update(String phone, String name) { SQLiteDatabase db = helper.getWritableDatabase(); db.execSQL("update contactinfo set phone = ? where name = ?;", new Object[]{phone, name}); // 记得关闭数据库,释放资源 db.close(); } /** * 查询联系人的电话号码 * * @param name 联系人姓名 */ public String query(String name) { SQLiteDatabase db = helper.getReadableDatabase(); Cursor cursor = db.rawQuery("select phone from contactinfo where name = ?", new String[]{name}); String phone = null; if (cursor.moveToNext()) { phone = cursor.getString(0); } // 记得关闭数据库,释放资源 cursor.close(); db.close(); return phone; } }

笔记批注:

SQLiteOpenHelper是个抽象类,里面有2个抽象方法onCreate()和onUpdate(),我们必须在自己的帮助类里面重写这2个方法,然后分别在这两个方法中实现创建、升级数据库逻辑。

SQLiteOpenHelper还有2个非常重要的实例方法getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建或者打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写的对象。

不同的是,当数据库不可写入的时候(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法将抛出异常。

    构建出SQLiteOpenHelper的实例之后,在调用它的getReadableDatabase()和getWritableDatabase()就能够创建数据库了。数据库文件在/data/data/<package name>/databases目录下。

sqlite中是不支持删除列操作的,所以网上 alter table [table_name] drop column [col_name] 这个语句在sqlite中是无效的(这不是MySQL),而替代的方法可以如下: 1.根据原表创建一张新表 2.删除原表

3.将新表重名为旧表的名称

慎用create table as select,比如想删除一列phone

 create table aa(id integer primary key autoincrement, name char(20), phone varchar(20));  create table temp as select id, name from aa;

新表中没有旧表中的primary key,Extra,auto_increment等属性,需要自己手动加,具体参看后面的修改表即字段属性.

那么新表temp就没了主键,不会自动增长,查看建表语句integer变成了int, char变成text。

只能创建类似于这样给出明确约束的

CREATE TABLE temp(id INTEGER PRIMARY KEY AUTOINCREMENT, NAME CHAR(20));

 

MyDBOpenHelper.java

import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; import android.widget.Toast; public class MyDBOpenHelper extends SQLiteOpenHelper { private String TAG = "MyDBOpenHelper"; private Context mContext; // 第一个参数是上下文 // 第二个参数是数据库名称 // 第三个参数null表示使用默认的游标工厂 // 第四个参数是数据库的版本号,数据库只能升级,不能降级,版本号只能变大不能变小 public MyDBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); // 更改一下版本号会调用onUpgrade mContext = context; } // 当数据库第一次被创建的时候调用的方法,适合在这个方法里面把数据库的表结构定义出来 // 当app再次启动会发现已经存在mydb.db数据库了,因此不会再创建一次 @Override public void onCreate(SQLiteDatabase db) { Log.d(TAG, "数据库被创建了: "); // MySQL是AUTO_INCREMENT, SQLite是AUTOINCREMENT db.execSQL("CREATE TABLE contactinfo(id INTEGER PRIMARY KEY AUTOINCREMENT, NAME CHAR(20), phone VARCHAR(20));"); Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show(); } // 当数据库更新的时候调用的方法 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.d(TAG, "数据库被更新了: "); //db.execSQL("ALTER TABLE contactinfo ADD account VARCHAR(20);"); db.execSQL("drop table if exists contactinfo"); onCreate(db); } }

笔记批注:

当我们重新运行程序时,数据库因为已经存在,不会再次创建,所以这个onCreate方法不会再次调用,怎么办呢?当然卸载程序再次运行也可以,这样的做法比较极端。这里就可以用到SQLiteOpenHelper的升级功能了。

db.execSQL("drop table if exists contactinfo");

onCreate(db);

如果存在contactinfo表就删除掉,然后再次调用onCreate方法,如果没有删除直接onCreate,那么系统会发现这张表存在,直接报错。

那么如何让onUpdate()方法能够执行呢?我们这里的MyDBOpenHelper构造器第四个参数是当前数据库的版本号,之前传入的是1,现在只要传入一个比1大的数字即可运行onUpdate方法。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/et_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:ems="10" android:hint="请输入联系人的姓名" android:inputType="textPersonName" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/et_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="12dp" android:ems="10" android:hint="请输入联系人的电话" android:inputType="number" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/et_name" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:onClick="add" android:text="添加" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/et_phone" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:onClick="delete" android:text="删除" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/button" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:onClick="update" android:text="修改" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/button2" /> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:onClick="query" android:text="查询" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/button3" /> </android.support.constraint.ConstraintLayout>

当输入数据然后点击添加的时候数据库会被创建(如果数据库还没被创建),数据会添加成功

我们可以把数据库导出到SQLite Expert去查看表内容,也可以直接在控制台查看一个大概,查看数据库和表有没有被创建。

这里只演示在控制台操作。

在Terminal或者在系统控制台输入adb shell

然后进行如下操作:

​​​​

用cd命令进入到/data/data/com.example.createdb2/databases 目录

注意:7.0及以上的模拟器无法进入com.xxxxxx.xxxxx,没有权限,示例只能在6.0及以下,当然,我们是可以直接找到mydb.db导出,然后用SQLite Expert去查看就更好了。在这里只是演示以下控制台该怎么做。

这个目录中,mydb.db是我们创建的

另一个是mydb.db-journal,这是为了让数据库能够支持事务而产生的临时日志文件,通常情况下这个文件的大小是0字节

接下来输入sqlite3 mydb.db 打开mydb.db数据库

输入.table命令查看数据库中有哪些表,这个android_metadata是每个数据库中都会自动生成的,不用管。

另一张contactinfo是我们在MyDBOpenHelper中创建的。

接着可以用.schema命令查看它们的建表语句。

最后可以输入.exit或.quit命令退出数据库的编辑,再键入exit就可以退出设备控制台了。

​​​​

​​​​

也可以直接写sql语句查询,如图

​​​

​​​

这里数据库版本是2

补充知识点:改变dos编码方式:chcp 936       //变成GBK编码

chcp 65001     //变成UTF-8编码

 

利用SQLiteDatabase中自带的增删改查操作:

ContactInfoDao.java

import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.example.createdb3.MyDBOpenHelper; public class ContactInfoDao { private final MyDBOpenHelper helper; private String TAG = "ContactInfoDao"; public ContactInfoDao(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { helper = new MyDBOpenHelper(context, name, factory, version); } /** * create table aa(id integer primary key autoincrement, name char(20), phone varchar(20)); * create table temp as select id, name from aa; // * temp表没有了PRIMARY KEY AUTOINCREMENT,查看建表语句CREATE TABLE "temp"(id INT,NAME TEXT); * integer变成了int * char变成text * 新表中没有旧表中的primary key,Extra,auto_increment等属性,需要自己手动加,具体参看后面的修改表即字段属性. * 添加一条记录 * * @param name 联系人姓名 * @param phone 联系人电话 * @return 返回的是添加在数据库的行号,-1代表失败 */ public long add(String name, String phone) { SQLiteDatabase db = helper.getWritableDatabase(); // 如果数据库已存在就打开,否则创建一个新数据库 // db.execSQL("insert into contactinfo (name, phone) values(?, ?)", new Object[]{name, phone}); ContentValues values = new ContentValues(); values.put("name", name); values.put("phone", phone); long rowId = db.insert("contactinfo", null, values); // 记得关闭数据库,释放资源 db.close(); return rowId; } /** * 根据姓名删除一条记录 * * @param name 联系人姓名 * @return 返回0代表的是没有做任何记录,返回的整数int值代表删除了几条数据 */ public int delete(String name) { SQLiteDatabase db = helper.getWritableDatabase(); // db.execSQL("delete from contactinfo where name = ?", new Object[]{name}); int rowId = db.delete("contactinfo", "name=?", new String[]{name}); // 记得关闭数据库,释放资源 db.close(); return rowId; } /** * 修改联系人电话号码 * * @param name 联系人姓名 * @param phone 联系人新电话 * @return rowId代表更新了多少行记录 */ public int update(String phone, String name) { SQLiteDatabase db = helper.getWritableDatabase(); // db.execSQL("update contactinfo set phone = ? where name = ?;", new Object[]{phone, name}); ContentValues values = new ContentValues(); values.put("phone", phone); int rowId = db.update("contactinfo", values, "name = ?", new String[]{name}); // 记得关闭数据库,释放资源 db.close(); return rowId; } /** * 查询联系人的电话号码 * * @param name 联系人姓名 * @return 电话号码 */ public Cursor query(String name) { SQLiteDatabase db = helper.getReadableDatabase(); // Cursor cursor = db.rawQuery("select phone from contactinfo where name = ?", new String[]{name}); /*Cursor cursor = db.query("contactinfo", new String[]{"phone"}, "name = ?", new String[]{name}, null, null, null); String phone = null; if (cursor.moveToNext()) { phone = cursor.getString(0); }*/ Cursor cursor = db.query("contactinfo", null, "name = ?", new String[]{name}, null, null, null); // 记得关闭数据库,释放资源 // db.close();// 当用ContentProvider返回一个Cursor时,db是不能关闭的 // 否则抛出java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed. return cursor; } }

再把MainActivity.java里面的query()方法改掉就行了

/** * 查询联系人号码 * * @param view */ public void query(View view) { String name = et_name.getText().toString().trim(); if (TextUtils.isEmpty(name)) { Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show(); return; } else { Cursor cursor = dao.query(name); String phone = null; StringBuffer str = new StringBuffer(); if (cursor.moveToFirst()) { // 将光标移动到第一行,如果游标为空,此方法将返回false。 String str1 = null; do { phone = cursor.getString(cursor.getColumnIndex("phone")); str1 = "name:" + name + " phone:" + phone; Log.d(TAG, str1); str.append(str1 + "\n"); } while (cursor.moveToNext());// 将光标移动到下一行,如果游标已经超过结果集中的最后一个条目,此方法将返回false。 str.deleteCharAt(str.length() - 1); // StringBuffer没有trim() } cursor.close(); if (phone != null) { Toast.makeText(this, "查询到的联系人信息为:\n" + str, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "无此联系人信息", Toast.LENGTH_SHORT).show(); } } }

​​​

添加同一个人多次时可以一次查出来。

注意:当用ContentProvider返回一个Cursor时,db是不能关闭的,否则抛出异常java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.

笔记批注:

public long insert (String table, String nullColumnHack, ContentValues values)

参数介绍:

table: 要插入数据的表的名称

nullColumnHack:当values参数为空或者里面没有内容的时候,我们insert是会失败的(底层数据库不允许插入一个空行),为了防止这种情况,我们要在这里指定一个列名,到时候如果发现将要插入的行为空行时,就会将你指定的这个列名的值设为null,然后再向数据库中插入。

values:一个ContentValues对象,类似一个map.通过键值对的形式存储值。

 

这里很多人会迷惑,nullColumnHack到底干什么用的,为什么会出现呢。当我们不设定一列的时候,不都是数据库给设为默认值吗?很多字段设置默认值也是null,这里显示的设置也是null,有什么区别吗,怎么会显示设置了之后就允许插入了呢?

其实在底层,各种insert方法最后都回去调用insertWithOnConflict方法,这里我们粘贴出该方法的部分实现

 

/**      * General method for inserting a row into the database.      *      * @param table the table to insert the row into      * @param nullColumnHack SQL doesn't allow inserting a completely empty row,      *            so if initialValues is empty this column will explicitly be      *            assigned a NULL value      * @param initialValues this map contains the initial column values for the      *            row. The keys should be the column names and the values the      *            column values      * @param conflictAlgorithm for insert conflict resolver      * @return the row ID of the newly inserted row      * OR the primary key of the existing row if the input param 'conflictAlgorithm' =      * {@link #CONFLICT_IGNORE}      * OR -1 if any error      */      public long insertWithOnConflict(String table, String nullColumnHack,              ContentValues initialValues, int conflictAlgorithm) {          if (!isOpen()) {              throw new IllegalStateException("database not open");          }             // Measurements show most sql lengths <= 152          StringBuilder sql = new StringBuilder(152);          sql.append("INSERT");          sql.append(CONFLICT_VALUES[conflictAlgorithm]);          sql.append(" INTO ");          sql.append(table);          // Measurements show most values lengths < 40          StringBuilder values = new StringBuilder(40);             Set<Map.Entry<String, Object>> entrySet = null;          if (initialValues != null && initialValues.size() > 0) {              entrySet = initialValues.valueSet();              Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();              sql.append('(');                 boolean needSeparator = false;              while (entriesIter.hasNext()) {                  if (needSeparator) {                      sql.append(", ");                      values.append(", ");                  }                  needSeparator = true;                  Map.Entry<String, Object> entry = entriesIter.next();                  sql.append(entry.getKey());                  values.append('?');              }                 sql.append(')');          } else {              sql.append("(" + nullColumnHack + ") ");              values.append("NULL");          }  

 这里我们可以看到,当我们的ContentValues类型的数据initialValues为null,或者size<=0时,就会再sql语句中添加nullColumnHack的设置。我们可以想象一下,如果我们不添加nullColumnHack的话,那么我们的sql语句最终的结果将会类似insert into tableName()values();这显然是不允许的。而如果我们添加上nullColumnHack呢,sql将会变成这样,insert into tableName (nullColumnHack)values(null);这样很显然就是可以的。  

 

 

public int delete (String table, String whereClause, String[] whereArgs)

    删除数据库中行的方便方法。

   table:要从其中删除的表

    whereClause:删除时要应用的可选WHERE子句。传递NULL将删除所有行。

    whereArgs:您可以在WHERE子句中包括?s,该子句将由WHERE Args的值替换。这些值将被绑定为String。

public int update (String table, ContentValues values, String whereClause, String[] whereArgs)

    更新数据库中行的方便方法。

    table:要更新的表

    values:从列名到新列值的映射。NULL是将被转换为NULL的有效值。

    whereClause:更新时要应用的可选WHERE子句。传递NULL将更新所有行。

    whereArgs: 您可以在WHERE子句中包括?s,该子句将由WHERE Args的值替换。这些值将被绑定为String。

public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy,                  String having, String orderBy)

    查询给定的URL,返回Cursor结果集。

    table:要编译查询的表名。

    columns:返回哪些列的列表。传递NULL将返回所有列,这是不鼓励的,以防止从存储区读取不被使用的数据。

    selection:一个过滤器,声明要返回的行,格式化为SQLWHERE子句(不包括WHERE本身)。传递NULL将返回给定表的所有行。

    selectionArgs:您可以在选择中包括?s,它将被selectionArgs的值替换,以便它们出现在所选内容中。这些值将被绑定为String。

    groupBy:一个过滤器,声明如何分组行,格式化为SQL GROUP BY子句(本身不包括组)。传递NULL将导致行不被分组。

    having:如果正在使用行分组,则筛选器将声明要在游标中包含哪些行组,格式为SQL HARING子句(不包括HAVING本身)。传递NULL将导致包括所有行组,并且在不使用行分组时是必需的。

    orderBy:如何对行进行排序,格式化为SQLOrderBy子句(不包括Order本身)。传递NULL将使用默认排序顺序,排序顺序可能是无序的。

    query有4个重载方法,建议查官方api。

SQLite数据库的事务介绍:

MainActivity.java

import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private String TAG = "MainActivity"; private MyOpenHelper helper; private EditText editText1, editText2, editText3; private String table = "info"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); helper = new MyOpenHelper(this, table, null, 1); editText1 = (EditText) findViewById(R.id.editText1); editText2 = (EditText) findViewById(R.id.editText2); editText3 = (EditText) findViewById(R.id.editText3); } public String queryColumn(Cursor cursor, String s) { String ss = null; if (cursor.moveToFirst()) { // 必须moveToFirst()否则异常 ss = cursor.getString(cursor.getColumnIndex(s)); } return ss; } public void onclick(View view) { SQLiteDatabase db = helper.getReadableDatabase(); String name1 = editText1.getText().toString().trim(); String name2 = editText2.getText().toString().trim(); String str = editText3.getText().toString().trim(); // 使用事务进行转账 db.beginTransaction(); // 开启事务 try { Cursor cursor = db.query(table, new String[]{"money"}, "name = ?", new String[]{name1}, null, null, null); int money = Integer.valueOf(queryColumn(cursor, "money")); // 实现转账的逻辑,实际就是写sql语句 //db.execSQL("update info set money = money - ? where name = ?", new Object[]{str, name1}); ContentValues values = new ContentValues(); int remain = money - Integer.valueOf(str); if (remain < 0) { Toast.makeText(this, "您的余额不足,转账失败", Toast.LENGTH_SHORT).show(); return; } values.put("money", remain + ""); db.update(table, values, "name = ?", new String[]{name1}); // int i = 9 / 0; // 让事务回滚示例 // db.execSQL("update info set money = money + ? where name = ?", new Object[]{str, name2}); cursor = db.query(table, new String[]{"money"}, "name = ?", new String[]{name2}, null, null, null); int money1 = Integer.valueOf(queryColumn(cursor, "money")); ContentValues values1 = new ContentValues(); int remain1 = money1 + Integer.valueOf(str); if (remain1 < 0) { return; } values1.put("money", remain1 + ""); db.update(table, values1, "name = ?", new String[]{name2}); // 转账之后的cursor cursor = db.query(table, new String[]{"money"}, "name = ?", new String[]{name1}, null, null, null); String query1 = queryColumn(cursor, "money"); cursor = db.query(table, new String[]{"money"}, "name = ?", new String[]{name2}, null, null, null); String query2 = queryColumn(cursor, "money"); cursor.close(); Log.d(TAG, name1 + "账户余额:" + query1 + "\n"); Log.d(TAG, name2 + "账户余额:" + query2 + "\n"); Toast.makeText(this, name1 + "账户余额:" + query1 + "\n" + name2 + "账户余额:" + query2, Toast.LENGTH_LONG).show(); // 给当前事务设置一个成功的标记 db.setTransactionSuccessful(); } catch (Exception e) { // 有catch不至于程序崩溃 Toast.makeText(this, "服务器忙,请稍后再试", Toast.LENGTH_SHORT).show(); } finally { db.endTransaction(); // 关闭事务,如果未执行setTransactionSuccessful,则回滚 } } }

 MyOpenHelper.java

import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class MyOpenHelper extends SQLiteOpenHelper { private String TAG = "MyOpenHelper"; public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } /** * 当数据库第一次创建时调用,特别适合用于表的初始化 * @param db */ @Override public void onCreate(SQLiteDatabase db) { Log.d(TAG, "数据库被创建了,onCreate里面开始建表 "); db.execSQL("create table info (_id integer primary key autoincrement, name varchar(20), phone varchar(20), money varchar(20))"); db.execSQL("insert into info ('name', 'phone', 'money') values('zhangsan', '138888', '2000')"); db.execSQL("insert into info ('name', 'phone', 'money') values('lisi', '139999', '4000')"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("drop table if exists info"); onCreate(db); } }

activity_mainxml

<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" android:onClick="onclick" android:text="转账" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/editText3" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" android:hint="请输入转账金额" android:inputType="number" app:layout_constraintBottom_toTopOf="@+id/button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/editText2" /> <EditText android:id="@+id/editText2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" android:ems="10" android:hint="请输入收款人姓名" android:inputType="textPersonName" app:layout_constraintBottom_toTopOf="@+id/editText1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/editText3" /> <EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" android:ems="10" android:hint="请输入转账人姓名" android:inputType="textPersonName" app:layout_constraintBottom_toTopOf="@+id/editText2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>

​​

=========================Talk is cheap, show me the code=========================

转载请注明原文地址: https://www.6miu.com/read-2627104.html

最新回复(0)