将.sqlite文件转换为.db文件,用于Android中的ROOM预填充数据库

yuvru6vn  于 2023-01-03  发布在  Android
关注(0)|答案(1)|浏览(381)

我开始在Android ROOM持久化库中工作,并遇到了Pre-Populating database。在本文中,他们提到了将.db文件添加到assets文件夹中。但问题是如何在线将.sqlite文件转换为.db文件。
这是一个全新的项目,所有的数据都是预先加载的,只有读操作。我尝试使用房间库来使用它,但是失败了。
请帮助我在获得最好的方法来转换.sqlite文件到.db文件或任何其他方式编程这样做。

4ktjp1zp

4ktjp1zp1#

您需要做的是基于XAMPP数据库创建一个SQLite数据库,使用XAMPP导出XAMPP数据库和SQLite工具(Navicat、DBeaver、DB Browser for SQlite、SQlite Studio),然后可以将生成的文件复制到assets文件夹中。

但是Room在模式期望方面非常严格,并且不支持SQLite列类型的灵活性。因此,建议您首先定义@Entity注解类(表和索引)然后提供X1 M1 N1 X注解类,其中X1 M2 N1 X注解的实体参数指定X1 M3 N1 X注解类。在这个阶段,编译项目将为表和索引生成SQL,因此将是Room所期望的表。

也许考虑一下这个例子,XAMPP数据库(例子):-

第一步是创建@Entity注解类(由于您尚未指定语言,因此使用了Java)。因此,在Android Studio的项目中:-
班级联系人:-

  1. @Entity
  2. class Contact {
  3. @PrimaryKey
  4. Long contact_id=null; /* XAAMPP INT no need for AUTOINCREMENT i.e. autoGenerate= true in the @PrimaryKey annotation */
  5. String contact_type; /* XAMPP VARCHAR */
  6. String contact_detail; /* XAMPP TEXT */
  7. String contact_notes;
  8. }
  • 因此VARCHAR和TEXT等同于字符串成员/字段
  • 任何整数类型可以是int、Integer、long、Long、byte、Byte,所有这些类型在由Room处理时最终都是SQLite类型INTEGER。
  • SQLite的自动增量效率低,不需要参见https://www.sqlite.org/autoinc.html
  • 长.... =空;只要是缺省值为0的基元,就一直使用。如果INTEGER PRIMARY KEY(rowid的别名)没有值(null),则会自动生成值,从而有效地实现AUTOINCREMENT。
  • Room将仅生成列类型INTEGER(整数类型)、真实的(浮点类型)、TEXT(字符串类型)和BLOB(字节数组/流类型)以及预填充数据库中的任何其他类型,并将导致异常(预期....发现....)。

班级学生:-

  1. @Entity
  2. class Student {
  3. @PrimaryKey
  4. Long student_id=null;
  5. @NotNull
  6. String student_forename;
  7. @NotNull
  8. String student_surname;
  9. @NotNull
  10. String student_street_address;
  11. @NotNull
  12. String student_city;
  13. @NotNull
  14. String student_county;
  15. @NotNull
  16. String student_country;
  17. @NotNull
  18. String student_dob;
  19. @NotNull
  20. String student_email;
  21. Student(){}
  22. @Ignore
  23. Student(long id,String student_forename, String student_surname,String student_street_address, String student_city, String student_county, String student_country, String student_dob, String student_email) {
  24. this.student_forename = student_forename;
  25. this.student_surname = student_surname;
  26. this.student_street_address = student_street_address;
  27. this.student_city = student_city;
  28. this.student_county = student_county;
  29. this.student_country = student_country;
  30. this.student_dob = student_dob;
  31. this.student_email = student_email;
  32. }
  33. @Ignore
  34. Student(String student_forename, String student_surname, String student_street_address, String student_city, String student_county, String student_country, String student_dob, String student_email) {
  35. this.student_forename = student_forename;
  36. this.student_surname = student_surname;
  37. this.student_street_address = student_street_address;
  38. this.student_city = student_city;
  39. this.student_county = student_county;
  40. this.student_country = student_country;
  41. this.student_dob = student_dob;
  42. this.student_email = student_email;
  43. }
  44. }

StudentContactMap(为实现引用完整性而添加的外键):-

  1. @Entity(
  2. tableName = "student_contact_map" /* name of the table different to class - not really needed - example of how to */
  3. , primaryKeys = {"student_map","contact_map"} /* composite primary key as can't have multiple @PrimaryKey annotations */
  4. /* Optional Foreign Keys to enforce referential integrity */
  5. , foreignKeys = {
  6. @ForeignKey(
  7. entity = Student.class,
  8. parentColumns = {"student_id"},
  9. childColumns = {"student_map"},
  10. /* Optional but simplifies referential integrity maintenance */
  11. onDelete = ForeignKey.CASCADE,
  12. onUpdate = ForeignKey.CASCADE
  13. ),
  14. @ForeignKey(
  15. entity = Contact.class,
  16. parentColumns = {"contact_id"},
  17. childColumns = {"contact_map"},
  18. onDelete = ForeignKey.CASCADE,
  19. onUpdate = ForeignKey.CASCADE
  20. )
  21. }
  22. )
  23. class StudentContactMap {
  24. long student_map;
  25. /* Added index as Room will warn about full table scan potential */
  26. @ColumnInfo(index = true)
  27. long contact_map;
  28. }

此阶段不需要@Dao注解接口(可以是抽象类)AllDAO(可以有多个,但为了方便起见,只能有一个):-

  1. @Dao
  2. interface AllDAOs {
  3. @Insert(onConflict = OnConflictStrategy.IGNORE)
  4. Long insert(Contact contact);
  5. @Insert(onConflict = OnConflictStrategy.IGNORE)
  6. Long insert(Student student);
  7. @Insert(onConflict = OnConflictStrategy.IGNORE)
  8. Long insert(StudentContactMap studentContactMap);
  9. @Query("SELECT * FROM student")
  10. List<Student> getAllStudents();
  11. @Query("SELECT * FROM contact")
  12. List<Contact> getAllContacts();
  13. @Query("SELECT * FROM student_contact_map")
  14. List<StudentContactMap> getAllStudentContactMaps();
  15. }

一个@Database注解类,包括获取数据库示例的单例方法,TheDatabase(显然可以用其他名称命名):-

  1. @Database(entities = {Contact.class,Student.class,StudentContactMap.class}, exportSchema = false, version = 1)
  2. abstract class TheDatabase extends RoomDatabase {
  3. /* method to retrieve the databases @Dao annotated interface (can have multiple such interfaces/abstract classes) */
  4. abstract AllDAOs getAllDAOs();
  5. /* For singleton approach */
  6. private static volatile TheDatabase INSTANCE;
  7. static TheDatabase getInstance(Context context) {
  8. if (INSTANCE==null) {
  9. INSTANCE = Room.databaseBuilder(context,TheDatabase.class,"the_database.db")
  10. .allowMainThreadQueries() /* for convenince of demo */
  11. .createFromAsset("the_database.db") /* asset name can be different to database name */
  12. .build();
  13. }
  14. return INSTANCE;
  15. }
  16. }

编译以生成预期的架构(SQL)

Ctrl + F9将编译项目。从Android Viewjava(生成的)将包含一个与@Database注解类同名但后缀为**_Impl* 的类。
在这个类中,有一个方法createAllTables,它使用SQL为Room自己创建的每个组件(表索引)创建SQL,即Room期望找到的EXACT模式。

现在,您可以在SQLite工具(使用Navicat)中创建预填充数据库。
对于每个组件(除room_master_table之外的表/索引),将SQL从生成的java复制到空连接/数据库和允许您运行SQL的窗口/视图(您可以运行SQL),例如:-

正在加载数据

切换到XAMPP并将数据库导出为SQL,例如在记事本或任何您可以得到的东西:-

  1. SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
  2. START TRANSACTION;
  3. SET time_zone = "+00:00";
  4. /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
  5. /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
  6. /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
  7. /*!40101 SET NAMES utf8mb4 */;
  8. --
  9. -- Database: `example`
  10. --
  11. -- --------------------------------------------------------
  12. --
  13. -- Table structure for table `contact`
  14. --
  15. CREATE TABLE `contact` (
  16. `contact_id` int(11) NOT NULL,
  17. `contact_type` varchar(16) NOT NULL,
  18. `contact_detail` text NOT NULL,
  19. `contact_notes` text NOT NULL
  20. ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
  21. --
  22. -- Dumping data for table `contact`
  23. --
  24. INSERT INTO `contact` (`contact_id`, `contact_type`, `contact_detail`, `contact_notes`) VALUES
  25. (1, 'Mobile', '0000 000 000', ''),
  26. (3, 'Mobile', '1111 111 111', ''),
  27. (4, 'Mobile', '2222 222 222', '');
  28. ....

由于表是根据Room的预期创建的,因此所需的全部工作就是使用insert语句填充它们,从而编辑导出SQL:-

  1. --
  2. -- Dumping data for table `contact`
  3. --
  4. INSERT INTO `contact` (`contact_id`, `contact_type`, `contact_detail`, `contact_notes`) VALUES
  5. (1, 'Mobile', '0000 000 000', ''),
  6. (3, 'Mobile', '1111 111 111', ''),
  7. (4, 'Mobile', '2222 222 222', '');
  8. --
  9. -- Dumping data for table `student`
  10. --
  11. INSERT INTO `student` (`student_id`, `student_forename`, `student_surname`, `student_street_address`, `student_city`, `student_county`, `student_country`, `student_dob`, `student_email`) VALUES
  12. (1, 'Fred', 'Blogs', '1 Somewhere Place', 'London', 'Middlesex', 'England', '2002-03-21', 'fblogs@student_mail.org'),
  13. (2, 'Jane', 'Doe', 'Nowhere House', 'Henley on Thames', 'Berkshire', 'England', '2001-08-11', 'jdoe@student_mail.org');
  14. --
  15. -- Dumping data for table `student_contact_map`
  16. --
  17. INSERT INTO `student_contact_map` (`student_map`, `contact_map`) VALUES
  18. (1, 1),
  19. (1, 2),
  20. (2, 3);

只是以防万一,而不是只有INSERT INTO的插入更改为INSERT OR IGNORE INTO
运行SQL的结果为:-

  1. INSERT OR IGNORE INTO `contact` (`contact_id`, `contact_type`, `contact_detail`, `contact_notes`) VALUES
  2. (1, 'Mobile', '0000 000 000', ''),
  3. (2, 'Mobile', '1111 111 111', ''),
  4. (3, 'Mobile', '2222 222 222', '')
  5. > Affected rows: 3
  6. > Time: 0.024s
  7. --
  8. -- Dumping data for table `student`
  9. --
  10. INSERT OR IGNORE INTO `student` (`student_id`, `student_forename`, `student_surname`, `student_street_address`, `student_city`, `student_county`, `student_country`, `student_dob`, `student_email`) VALUES
  11. (1, 'Fred', 'Blogs', '1 Somewhere Place', 'London', 'Middlesex', 'England', '2002-03-21', 'fblogs@student_mail.org'),
  12. (2, 'Jane', 'Doe', 'Nowhere House', 'Henley on Thames', 'Berkshire', 'England', '2001-08-11', 'jdoe@student_mail.org')
  13. > Affected rows: 2
  14. > Time: 0.024s
  15. --
  16. -- Dumping data for table `student_contact_map`
  17. --
  18. INSERT OR IGNORE INTO `student_contact_map` (`student_map`, `contact_map`) VALUES
  19. (1, 1)
  20. ,(1, 2)
  21. ,(2, 3)
  22. > Affected rows: 3
  23. > Time: 0.024s

即所有数据均已按预期成功插入。
保存数据库,检查是否只存在一个文件,以及是否没有与数据库同名且后缀为-wal或-shm的文件(可以忽略-journal文件)。

  • -wal/-shm文件是在数据库使用“预写日志”时使用的文件。-wal文件包含数据库的一部分。关闭数据库应将-wal写入数据库,并清空和删除-wal文件(使用Navicat时,必须退出Navicat)。

因此,最终结果是一个文件,即数据库,例如:-

然后可以将此文件复制并粘贴到assets文件夹中,例如:-

  • 注意文件已相应地重命名
    测试时间

要测试预填充数据库的副本,必须获取@Database注解类的示例,并且必须尝试访问数据库(只有在尝试访问数据库时才会打开数据库,获取示例不会打开数据库)。因此,需要一些活动代码,例如:-

  1. public class MainActivity extends AppCompatActivity {
  2. TheDatabase dbInstance;
  3. AllDAOs dao;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. dbInstance = TheDatabase.getInstance(this);
  9. dao = dbInstance.getAllDAOs();
  10. for (Student s: dao.getAllStudents()) {
  11. Log.d("DBINFO","Student is " + s.student_forename + " " + s.student_surname + " etc");
  12. }
  13. }
  14. }

运行时,日志包括:-

  1. D/DBINFO: Student is Fred Blogs etc
  2. D/DBINFO: Student is Jane Doe etc

成功

展开查看全部

相关问题