【问题标题】:Using String.format() to create a sqlite table使用 String.format() 创建一个 sqlite 表
【发布时间】:2017-11-19 23:06:27
【问题描述】:

堆栈溢出,

我正在尝试了解如何使用 String.format() 为我的 Android 应用程序创建一个 sqlite 数据库。

我有一个扩展 SQLiteOpenHelper 的公共类和一个包含私有静态最终字符串的私有内部类,我打算将其作为数据库的列名。

音乐数据库类代码

public class SaturnMusicDatabase extends SQLiteOpenHelper{
    private Context mContext;

   // Necessary public constructor to use SQLiteOpenHelper 
    public SaturnMusicDatabase(Context context){
        super(context, "SaturnMusic.db"), null,1);
        mContext = context;
    }
    // The columns beginning with an underscore are the primary key
private static final class Songs{
     private static final String TABLE = "songs";
     private static final String COL_ARTIST ="_artist";
     private static final String COL_ALBUM="_album";
     private static final String COL_TITLE ="_title";
     private static final String COL_GENRE = "genre";
     private static final String COL_LENGTH ="length";
     private static final String COL_Number ="number";
 }
@Override
 public void onCreate(SQLiteDatabase db) {
     String queryMakeSong = String.format
             ("create table %s (primary key(%s, %s, %s)%s, %s, %s)",
                     Songs.TABLE, Songs.COL_ARTIST, Songs.COL_ALBUM, Songs.COL_TITLE,
                    Songs.COL_GENRE, Songs.COL_LENGTH, Songs.COL_Number);
     db.execSQL(queryMakeSong);
 }

 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 }
} 

我的困惑在于 create 语句。我理解 String.format() 工作原理的方式是,后跟修饰符的 % 符号决定了在格式化传递给函数的数据时要使用的数据类型。

我的目的是创建一个名为 song 的表,它有六个字段,其中三个是主键:Artist、Album 和 Title。

字段名称应该是字符串是有道理的,所以这就是我选择使用's'修饰符的原因。这是否意味着将存储在这些字段中的数据将是字符串?或者这是否意味着字段名称是字符串,我必须在 create table 语句中指定这些字段应包含的数据类型?

如果是前者,那不是我想要的所有领域。 LENGTH 和 NUMBER 之类的东西应该存储整数。这让我觉得他们应该使用“d”修饰符。

如果我的语法有错误,请分享,以及如何纠正它。

【问题讨论】:

    标签: java android sqlite string-formatting sqliteopenhelper


    【解决方案1】:

    我理解 String.format() 工作原理的方式是 % 符号 后跟修饰符确定格式化时使用的数据类型 传入函数的数据。

    我相信 % 之后是参数的索引,即 %1... 替换第一个参数 %2... 第二个等。

    在类型/转换之前是 $

    所以(猜测)你会一直在尝试:-

        ..... create table %1$s (primary key(%2$s, %3$s, %4$s)%5$s, %6$s, %7$s)"
    

    这是否意味着将存储在这些字段中的数据将 是字符串吗?

    一点也不。事实上,SQLite 非常灵活,您可以在任何列中存储任何类型的值(一个例外是 rowid 列,它始终是 TYPE INTEGER,它是由默认通常是透明的;将列定义为 ?? INTEGER PRIMARY KEY?? INTEGER PRIMARY KEY AUTOINCREMENT) 为名为 ?? 的 rowid 列创建别名。 (其中 ?? 将是一个有效的列名,例如 _id)。

    在您没有指定列类型的情况下,根据规则 (#3),列类型将全部为 BLOB -

    如果声明的列类型包含字符串“BLOB”或如果没有 指定类型,则该列具有亲和性 BLOB。

    但是,当实际存储一个值时,存储类开始发挥作用,并应用以下规则:-

    具有亲和性 BLOB 的列不喜欢一个存储类 另一个并且没有尝试从一个存储类中强制数据 进入另一个。

    即数据将根据所应用的数据类型进行存储。

    您可能希望查看上述内容所基于的Datatypes In SQLite Version 3

    以这个表为例:-

    可能有数据(图例描述了颜色编码):-

    这表明每个行/列组合实际上都有自己的 TYPE。


    或者这是否意味着字段名称是字符串,我必须指定 这些字段在 create table 语句中应该包含什么数据类型?

    如上所述,尽管您可能更愿意,但您不会这样做。

    我有一个包含私有静态 final 的私有内部类 字符串

    您可能会发现将名称命名为public static final String 会更有用。然后,您就有了可以在整个过程中使用的名称的单一定义。

    即您可能希望在某个时间访问数据,在这种情况下,您可能需要一个列名,例如从表中的该列获取数据。

    比较以下:-

    Cursor csr = db.query("snogs",null,null,null,null,null.null); // What idiot used snogs instead of songs?????
    
    Cursor csr = db.query(Songs.TABLE,null,null,null,null,null,null);
    

    前者 = table not found 错误,因为我无意中错误地闻到了(here I go again :))歌曲(显然是故意的)。

    对于许多 IDE,后者很难出错,因为 Snogs.TABLE 在编辑器中会显示为不正确并且无法编译。

    在检索数据不太可能导致问题时,使用列名(尤其是来自单一来源)而不是硬编码偏移量。所以不是

        String artist = csr.getString(5); // what column is this????
    

    越啰嗦:-

         String artist = csr.getString(csr.getColumnIndex(Songs.COL_Number));
    

    不太可能导致问题,例如:-

    假设您修改了表格以包含另一列并且该列位于最后一列之前,那么前者会得到错误的数据,直到更改为 6,而后者不需要更改。

    【讨论】:

      【解决方案2】:

      你不应该。请改用绑定变量。使用 String.format 会使您容易受到 SQL 注入安全漏洞的影响,绑定变量可以避免这种情况。如果您不执行动态查询(例如创建表),则只需输入原始 SQL 而不是执行格式,那么在维护查询时它将更具可读性。

      但是要回答您的问题-格式中的符号与存储在数据库中的数据类型无关,因为 String.format 对数据库一无所知,也不知道它被用于数据库查询.它只是输出格式化的字符串。如果您放入该字符串的实际值是一个数字,请使用 %d。如果它是一个字符串,如表名、列名等,请使用 %s。

      【讨论】:

      • 将此标记为已接受的答案。如果你有时间,你能举例说明绑定变量在这个问题的上下文中是如何工作的吗?
      • 绑定变量对列名不起作用,插入的字符串为常量时无法进行SQL注入。
      • @CL 重读我的评论。我说对于非动态查询只需使用 sql 作为原始语句。但是即使使用常量,您也应该尽可能使用绑定变量,它们可以更好地优化,并防止经验不足的程序员稍后将反应堆转换为动态数据的可能性
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-21
      • 2021-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多