I want to use SQLDelight to connect to a SQLite In-Memory database for test and to PostgreSQL server for production, is that use case possible? how I can do the config? and do I need to have .sq files for each database?
Yes, you can it.
First, create one directory for each database in the "src/main" directory of your module:
src/main/database1/
src/main/database2/
do I need to have .sq files for each database?
Yes, you need it. Put your .sq files into packege-associated directory:
src/main/database1/your/package/name/database1.sq
src/main/database2/your/package/name/database2.sq
Next, configure your databases in module's build.gradle file:
sqldelight {
database("Database1") {
packageName = "your.package.name"
sourceFolders = listOf("database1")
}
database("Database2") {
packageName = "your.package.name"
sourceFolders = listOf("database2")
}
}
It is important to specify the correct (created in the previous step) sourceFolders for each database.
Finally, implement the correct driver for each database as mentioned in the SqlDeLight documentation.
Rebuild your project before use databases. SqlDeLight will generate separated implementations classes for each database.
Related
I use sqflite in flutter because i am able to find the databse file and save it for many use later.
But when i started to use isar db i could not find the db file at all.
I tried path and path provider to find the directory like this
final directory = await getApplicationSupportDirectory();
final Isar isar = await Isar.open([IsarEmailSchema], directory: directory.path);
with no success.
So what is the isar database extension and how to find the db files acrros platforms?
The file extension for an ISAR (Integrated Station Automated Reporting) database is usually ".mdb", which stands for Microsoft Access Database.
The database file extension for Isar database is not specified. It is a self-contained database that operates on the file system and doesn't require a separate database server to function.
As per the information available, Isar uses SQLite database under the hood, but the actual file extension could be different as it might be using a custom wrapper on top of SQLite.
In order to find the Isar database file, you could try to use the getDatabasesPath method from the Flutter path_provider package to get the path to the database file, then append the name of the database file to it.
For example:
java
final directory = await getDatabasesPath();
final String dbPath = directory.path + "/isar_database.db";
final Isar isar = await Isar.open([IsarEmailSchema], dbPath);
This should give you the path to the Isar database file, which you can then use to locate the file across platforms.
I have a relatively large db that may take 1 to 2 minutes to initialise, is it possible to load a pre-populated db when using sqldelight (kotlin multiplatform) instead of initialising the db on app launch?
Yes, but it can be tricky. Not just for "Multiplatform". You need to copy the db to the db folder before trying to init sqldelight. That probably means i/o on the main thread when the app starts.
There is no standard way to do this now. You'll need to put the db file in assets on android and in a bundle on iOS and copy them to their respective folders before initializing sqldelight. Obviously you'll want to check if the db exists first, or have some way of knowing this is your first app run.
If you're planning on shipping updates that will have newer databases, you'll need to manage versions outside of just a check for the existance of the db.
Although not directly answering your question, 1 to 2 minutes is really, really long for sqlite. What are you doing? I would first make sure you're using transactions properly. 1-2 minutes of inserting data would (probably) result in a huge db file.
Sorry, but I can't add any comments yet, which would be more appropriate...
Although not directly answering your question, 1 to 2 minutes is
really, really long for sqlite. What are you doing? I would first make
sure you're using transactions properly. 1-2 minutes of inserting data
would (probably) result in a huge db file.
Alternatively, my problem due to which I had to use a pre-populated database was associated with the large size of .sq files (more than 30 MB text of INSERTs per table), and SqlDeLight silently interrupted the generation, without displaying error messages.
You'll need to put the db file in assets on android and in a bundle on
iOS and copy them to their respective folders before initializing
sqldelight.
Having to load a db from resources on both android and ios feels a lot
of work + it means the shared project wont be the only place where the
data is initialised.
Kotlin MultiPlatform library Moko-resources solves the issue of a single source for a database in a shared module. It works for KMM the same way for Android and iOS.
Unfortunately, using this feature are almost not presented in the samples of library. I added a second method (getDriver) to the expected class DatabaseDriverFactory to open the prepared database, and implemented it on the platform. For example, for androidMain:
actual class DatabaseDriverFactory(private val context: Context) {
actual fun createDriver(schema: SqlDriver.Schema, fileName: String): SqlDriver {
return AndroidSqliteDriver(schema, context, fileName)
}
actual fun getDriver(schema: SqlDriver.Schema, fileName: String): SqlDriver {
val database: File = context.getDatabasePath(fileName)
if (!database.exists()) {
val inputStream = context.resources.openRawResource(MR.files.dbfile.rawResId)
val outputStream = FileOutputStream(database.absolutePath)
inputStream.use { input: InputStream ->
outputStream.use { output: FileOutputStream ->
input.copyTo(output)
}
}
}
return AndroidSqliteDriver(schema, context, fileName)
}
}
MR.files.fullDb is the FileResource from the class generated by the library, it is associated with the name of the file located in the resources/MR/files directory of the commonMain module. It property rawResId represents the platform-side resource ID.
The only thing you need is to specify the path to the DB file using the driver.
Let's assume your DB lies in /mnt/my_best_app_dbs/super.db. Now, pass the path in the name property of the Driver. Something like this:
val sqlDriver: SqlDriver = AndroidSqliteDriver(Schema, context, "/mnt/my_best_app_dbs/best.db")
Keep in mind that you might need to have permissions that allow you to read a given storage type.
Lets say I have applicationA that has 3 property files:
-> applicationA
- datasource.properties
- security.properties
- jms.properties
How do I move all properties to a spring cloud config server and keep them separate?
As of today I have configured the config server that will only read ONE property file as this seems to be the standard way. This file the config server picks up seems to be resolved by using the spring.application.name. In my case it will only read ONE file with this name:
-> applicationA.properties
How can I add the other files to be resolved by the config server?
Not possible in the way how you requested. Spring Cloud Config Server uses NativeEnvironmentRepository which is:
Simple implementation of {#link EnvironmentRepository} that uses a SpringApplication and configuration files located through the normal protocols. The resulting Environment is composed of property sources located using the application name as the config file stem (spring.config.name) and the environment name as a Spring profile.
See: https://github.com/spring-cloud/spring-cloud-config/blob/master/spring-cloud-config-server/src/main/java/org/springframework/cloud/config/server/environment/NativeEnvironmentRepository.java
So basically every time when client request properties from Config Server it creates ConfigurableApplicationContext using SpringApplicationBuilder. And it is launched with next configuration property:
String config = application;
if (!config.startsWith("application")) {
config = "application," + config;
}
list.add("--spring.config.name=" + config);
So possible names for property files will be only application.properties(or .yml) and config client application name that is requesting configuration - in your case applicationA.properties.
But you can "cheat".
In config server configuration you can add such property
spring:
cloud:
config:
server:
git:
search-paths: '{application}, {application}/your-subdirectory'
In this case Config Server will search for same property file names but in few directories and you can use subdirectories to keep your properties separate.
So with configuration above you will be able to load configuration from:
applicationA/application.properies
applicationA/your-subdirectory/application.properies
This can be done.
You need to create your own EnvironmentRepository, which loads your property files.
org.springframework.cloud.config.server.support.AbstractScmAccessor#getSearchLocations
searches for the property files to load :
for (String prof : profiles) {
for (String app : apps) {
String value = location;
if (app != null) {
value = value.replace("{application}", app);
}
if (prof != null) {
value = value.replace("{profile}", prof);
}
if (label != null) {
value = value.replace("{label}", label);
}
if (!value.endsWith("/")) {
value = value + "/";
}
output.addAll(matchingDirectories(dir, value));
}
}
There you could add custom code, that reads the required property files.
The above code matches exactly the behaviour described in the spring docs.
The NativeEnvironmentRepository does NOT access GIT/SCM in any way, so you should use
JGitEnvironmentRepository as base for your own implementation.
As #nmyk pointed out, NativeEnvironmentRepository boots a mini app in order to collect the properties by providing it with - sort of speak - "hardcoded" {appname}.* and application.* supported property file names. (#Stefan Isele - prefabware.com JGitEnvironmentRepository ends up using NativeEnvironmentRepository as well, for that matter).
I have issued a pull request for spring-cloud-config-server 1.4.x, that supports defining additional file names, through a spring.cloud.config.server.searchNames environment property, in the same sense one can do for a single springboot app, as defined in the Externalized Configuration.Application Property Files section of the documentation, using the spring.config.name enviroment property. I hope they review it soon, since it seems many have asked about this feature in stack overflow, and surely many many more search for it and read the currently advised solutions.
It worths mentioning that many ppl advise "abusing" the profile feature to achieve this, which is a bad practice, in my humble opinion, as I describe in this answer
How to manage multiple projects dealing with the same DB schema. The Flyway migration scripts in each of the project does not allow to start if it is modified by the other project.
For example:
I have a spring boot Project X with a FlywayInitializer class.
#PostConstruct
public void migrateFlyway() {
final Flyway flyway = new Flyway();
flyway.setSchemas("schema1");
flyway.setLocations("classpath:x.migration");
flyway.migrate();
}
And i have a submodule Project Y with also his own FlywayInitializer class
#PostConstruct
public void migrateFlyway() {
final Flyway flyway = new Flyway();
flyway.setSchemas("schema1");
flyway.setLocations("classpath:y.migration");
flyway.migrate();
}
Project Structure:
Project X
src
|
main
|
java
FlywayInitializerX.java
|
resources
V1.0_create_tableX.sql
V1.1_update_tableX.sql
Project Y
src
|
main
|
java
FlywayInitializerY.java
|
resources
V1.0_create_tableY.sql
V1.1_update_tableY.sql
How can i use the for both Project X and Y the same schemaname "schema1" with Flyway ?
EDIT:
Thanks #jesper_bk that helped me. Its exactly what i wanted, that the two projects have completely "independent lives" in the same schema. But now i have the following problem:
The first executed project X create tables correcty, but if Project Y is started i get the error Found non-empty schema without metadata table. So i have to set BaselineOnMigrate to true. But if i set BaselineOnMigrate to true the Project Y skip the sql file V1.0_create_tableY.sql and starts with V1.1_update_tableY.sql. How can i reach, that the first sql script V1.0_create_tableY.sql is also executed for Project Y?
#PostConstruct
public void migrateFlyway() {
final Flyway flyway = new Flyway();
flyway.setBaselineVersionAsString("1");
flyway.setBaselineOnMigrate(true);
flyway.setSchemas("schema1");
flyway.setLocations("classpath:y.migration");
flyway.migrate();
}
If you can live with two projects having completely "independent lives" in the same schema, you could use separate version tables for the two, i.e.:
#PostConstruct
public void migrateFlyway() {
final Flyway flyway = new Flyway();
flyway.setSchemas("schema1");
flyway.setLocations("classpath:x.migration");
flyway.setTable("schema_version_y");
flyway.migrate();
}
If you want them to use the same versioning scheme, you are probably better served with putting all SQL scripts in separate third project, or - even more complicated - have a third project that automatically gathers and enumerates the SQL scripts from the main project.
Concerning your second question, baselineVersionAsString should be < 1 (e.g. 0). If the baseline version is 1, it will determine that your first script with version 1.0 matches the baseline, and should already have been executed.
f any one using the Play frame work this problem can be tackle with independent flyway history tables for each microservice mean every mircoservice has its own flyway history table with name according to service. this ll create flyway table for each service add these properties in conf file for changing the flyway table name.
db.default.migration.table=microservice1 for 1 mircoservice
db.default.migration.table=microservice2 for 2 mircoservice
add this property in every microservice conf file this is only for play frame work
if using spring then We are using spring/flyway-db configuration so this was simply adding the following to application.properties for each project in addition to the first.
flyway.table=schema_version_*<some_other_identifier>*
I've written a simple method in Java:
package com.fidel.extensions;
public class Extensions {
public static String capitalize(String input) {
return input.toUpperCase();
}
}
I then registered it as a function in Apache Derby.
create function capitalize(inputString varchar(255))
returns varchar(255)
parameter style JAVA
no sql language JAVA
external name 'com.fidel.extensions.Extensions.capitalize'
In order to give the database access to that code, this page suggests I have two choices:
Install the jar into the database
Add the jar to CLASSPATH
This is the text from that article:
The compiled Java for a procedure (or function) may be stored in the
database using the standard SQL procedure SQLJ.INSTALL_JAR or may be
stored outside the database in the class path of the application.
If I use the INSTALL_JAR approach to embed the jar into the database, my queries work fine. For example:
select capitalize('hello') from SYSIBM.SYSDUMMY1
However I don't actually want to store the jar in the database. I would like derby to look in my CLASSPATH variable to find it.
So I've added it to my CLASSPATH using the following:
export CLASSPATH=${CLASSPATH}:/home/fidel/dev/DbExtensions/extensions.jar
But when I run the same query, I get this error message:
The class 'com.fidel.extensions.Extensions' does not exist or is
inaccessible.
I'm using Netbean's SQL editor, which I assume would pick up the CLASSPATH I've set.
Has anyone managed to reference code in an external jar, via the CLASSPATH?
ps. I know I can use the UCASE/UPPER methods. But the code above is just an example
pps. I am able to get the query to work by adding the jar to the Driver list, but I don't think that's the correct thing to do.
Services -> Drivers -> Java DB (Embedded) -> Customize -> Add