使用 String.format() 创建一个 sqlite table
Using String.format() to create a sqlite table
堆栈溢出,
我正在尝试了解如何使用 String.format() 为我的 Android 应用程序创建一个 sqlite 数据库。
我有一个扩展 SQLiteOpenHelper 的 public class 和一个包含私有静态最终字符串的私有内部 class,我打算将其作为我的数据库的列名。
音乐数据库Class代码
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() 工作原理的方式是,% 符号后跟修饰符决定了在格式化传递给函数的数据时要使用的数据类型。
我的目的是创建一个 table 命名歌曲,它有六个字段,其中三个是主键:艺术家、专辑和标题。
字段名称应该是字符串是有道理的,所以这就是我选择使用 's' 修饰符的原因。这是否意味着将存储在这些字段中的数据将是字符串?或者这是否意味着字段名称是字符串,我必须在 create table 语句中指定这些字段应包含的数据类型?
如果是前者,那不是我所有领域想要的。像 LENGTH 和 NUMBER 这样的东西应该存储整数。这让我认为他们应该使用 'd' 修饰符。
如果我的语法有任何错误,请分享,以及我如何纠正它。
你不应该。请改用绑定变量。使用 String.format 会使您容易受到 SQL 注入安全漏洞的攻击,绑定变量可以避免这种情况。如果您不执行动态查询(如创建 table),那么只需放入原始 SQL 而不是执行格式,这样在维护查询时会更易读。
但要回答你的问题——格式中的符号与数据库中存储的数据类型无关,因为 String.format 对数据库一无所知,或者它正在被使用用于数据库查询。它只是输出格式化的字符串。如果您放入该字符串的实际值是一个数字,请使用 %d。如果它是一个字符串,如 table 名称、列名称等,请使用 %s。
The way I understand how String.format() works is that the % sign
followed by a modifier determines the data type to use when formatting
the data that is passed into the function.
我相信 %
后跟参数的索引,即 %1...替换第一个参数 %2...第二个等
是 $
在 type/conversion 之前。
所以(猜测)你会一直在尝试:-
..... create table %1$s (primary key(%2$s, %3$s, %4$s)%5$s, %6$s, %7$s)"
Does this mean that the data that will be stored in these fields will
be strings?
完全没有。事实上,SQLite 非常灵活,您可以在任何列中存储任何类型的值(一个例外是 rowid 列,它始终是 TYPE INTEGER,默认情况下通常是透明的;将列定义为 ?? INTEGER PRIMARY KEY
或 ?? INTEGER PRIMARY KEY AUTOINCREMENT
) 会为名为 ?? 的 rowid 列创建一个别名(其中 ?? 将是有效的列名,例如 _id)。
在您没有指定列类型的情况下,列类型将全部为 BLOB,按照规则 (#3) -
If the declared type for a column contains the string "BLOB" or if no
type is specified then the column has affinity BLOB.
然而,当实际存储一个值时,Storage Class 开始发挥作用并应用以下规则:-
A column with affinity BLOB does not prefer one storage class over
another and no attempt is made to coerce data from one storage class
into another.
即数据将根据所应用的数据类型进行存储。
您可能希望查看以上所基于的 Datatypes In SQLite Version 3 。
例如这个 Table :-
可以根据(图例描述的颜色编码)获得数据:-
这表明每个 row/column 组合实际上都有自己的类型。
Or does it mean that the field names are strings and I have to specify
what data type these fields should hold in the create table statement?
如上所示,尽管您可能更愿意,但您不需要。
I have a private inner class that contains private static final
strings
您可能会发现将名称设为 public static final String
更有用。然后您就有了一个可以在整个过程中使用的名称定义。
即您可能想在某个时候访问数据,在这种情况下您可能需要一个列名,比如从 table.
中获取该列的数据
比较以下:-
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未发现错误是因为我无意中把(我又来了:))歌曲听错了(明显是故意的)
后者很难被许多 IDE 弄错,因为 Snogs.TABLE
在编辑器中会显示为不正确并且不会编译。
在检索数据时使用列名(尤其是来自单一来源的列名)而不是硬编码偏移量不太可能导致问题。所以而不是
String artist = csr.getString(5); // what column is this????
越啰嗦:-
String artist = csr.getString(csr.getColumnIndex(Songs.COL_Number));
不太可能导致问题,例如:-
假设您修改了 table 以包含另一列,并且该列位于最后一列之前,那么前者将得到错误的数据,直到更改为 6,而后者则不需要更改.
堆栈溢出,
我正在尝试了解如何使用 String.format() 为我的 Android 应用程序创建一个 sqlite 数据库。
我有一个扩展 SQLiteOpenHelper 的 public class 和一个包含私有静态最终字符串的私有内部 class,我打算将其作为我的数据库的列名。
音乐数据库Class代码
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() 工作原理的方式是,% 符号后跟修饰符决定了在格式化传递给函数的数据时要使用的数据类型。
我的目的是创建一个 table 命名歌曲,它有六个字段,其中三个是主键:艺术家、专辑和标题。
字段名称应该是字符串是有道理的,所以这就是我选择使用 's' 修饰符的原因。这是否意味着将存储在这些字段中的数据将是字符串?或者这是否意味着字段名称是字符串,我必须在 create table 语句中指定这些字段应包含的数据类型?
如果是前者,那不是我所有领域想要的。像 LENGTH 和 NUMBER 这样的东西应该存储整数。这让我认为他们应该使用 'd' 修饰符。
如果我的语法有任何错误,请分享,以及我如何纠正它。
你不应该。请改用绑定变量。使用 String.format 会使您容易受到 SQL 注入安全漏洞的攻击,绑定变量可以避免这种情况。如果您不执行动态查询(如创建 table),那么只需放入原始 SQL 而不是执行格式,这样在维护查询时会更易读。
但要回答你的问题——格式中的符号与数据库中存储的数据类型无关,因为 String.format 对数据库一无所知,或者它正在被使用用于数据库查询。它只是输出格式化的字符串。如果您放入该字符串的实际值是一个数字,请使用 %d。如果它是一个字符串,如 table 名称、列名称等,请使用 %s。
The way I understand how String.format() works is that the % sign followed by a modifier determines the data type to use when formatting the data that is passed into the function.
我相信 %
后跟参数的索引,即 %1...替换第一个参数 %2...第二个等
是 $
在 type/conversion 之前。
所以(猜测)你会一直在尝试:-
..... create table %1$s (primary key(%2$s, %3$s, %4$s)%5$s, %6$s, %7$s)"
Does this mean that the data that will be stored in these fields will be strings?
完全没有。事实上,SQLite 非常灵活,您可以在任何列中存储任何类型的值(一个例外是 rowid 列,它始终是 TYPE INTEGER,默认情况下通常是透明的;将列定义为 ?? INTEGER PRIMARY KEY
或 ?? INTEGER PRIMARY KEY AUTOINCREMENT
) 会为名为 ?? 的 rowid 列创建一个别名(其中 ?? 将是有效的列名,例如 _id)。
在您没有指定列类型的情况下,列类型将全部为 BLOB,按照规则 (#3) -
If the declared type for a column contains the string "BLOB" or if no type is specified then the column has affinity BLOB.
然而,当实际存储一个值时,Storage Class 开始发挥作用并应用以下规则:-
A column with affinity BLOB does not prefer one storage class over another and no attempt is made to coerce data from one storage class into another.
即数据将根据所应用的数据类型进行存储。
您可能希望查看以上所基于的 Datatypes In SQLite Version 3 。
例如这个 Table :-
可以根据(图例描述的颜色编码)获得数据:-
这表明每个 row/column 组合实际上都有自己的类型。
Or does it mean that the field names are strings and I have to specify what data type these fields should hold in the create table statement?
如上所示,尽管您可能更愿意,但您不需要。
I have a private inner class that contains private static final strings
您可能会发现将名称设为 public static final String
更有用。然后您就有了一个可以在整个过程中使用的名称定义。
即您可能想在某个时候访问数据,在这种情况下您可能需要一个列名,比如从 table.
中获取该列的数据比较以下:-
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未发现错误是因为我无意中把(我又来了:))歌曲听错了(明显是故意的)
后者很难被许多 IDE 弄错,因为 Snogs.TABLE
在编辑器中会显示为不正确并且不会编译。
在检索数据时使用列名(尤其是来自单一来源的列名)而不是硬编码偏移量不太可能导致问题。所以而不是
String artist = csr.getString(5); // what column is this????
越啰嗦:-
String artist = csr.getString(csr.getColumnIndex(Songs.COL_Number));
不太可能导致问题,例如:-
假设您修改了 table 以包含另一列,并且该列位于最后一列之前,那么前者将得到错误的数据,直到更改为 6,而后者则不需要更改.