I am creating a service which writes directly to a snowflake database.
I am having a lot of trouble trying to get spring data jpa to work effectively with Snowflake. My main issue is that I am unable to save an entity to the Snowflake DB through Jpa Repository interface Save method. Because this application is being used to dump data into Snowflake, being able to leverage JPA would make life a lot easier.
I would prefer not to have to roll my own native queries so my question is whether it's possible to leverage Hibernate when working with Snowflake.
The main thing I want to be able to do is persist entities using the Jpa Repositories inbuild Save method.
Below is my current configuration. Any ideas on what could be improved in the configuration to get this working would be appreciated, or also any opinion on whether it is possible or not.
spring:
profiles:
active: local
application:
name: Service
datasource:
driverClassName: net.snowflake.client.jdbc.SnowflakeDriver
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
flyway:
locations: classpath:db/migration/common,classpath:db/migration/snowflake
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.SQLServerDialect
order_inserts: true
create sequence award_event_id_seq;
create table award_event
(
id INT NOT NULL DEFAULT award_event_id_seq.nextval PRIMARY KEY,
event_source_system varchar not null,
event_trigger VARCHAR NOT NULL,
event_triggered_by VARCHAR NOT NULL,
event_timestamp TIMESTAMP NOT NULL
)
#Entity(name = "award_event")
#SequenceGenerator(name = "award_event_id_seq", sequenceName = "award_event_id_seq", allocationSize = 1)
data class AwardEvent(
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
val id: Int = -1,
val eventTrigger: String,
val eventTriggeredBy: String,
val eventTimestamp: LocalDateTime,
val eventSourceSystem: String
)
override fun receiveMessage(message: String) {
logger.info("Receiving award event: $message")
val awardEvent: AwardEventMessage = message.toObject()
// This Save method does not work and throws an error specified below
awardEventRepository.save(awardEvent.toAwardEvent())
}
2021-01-08 10:49:28.163 ERROR 3239 --- [nio-9106-exec-1] o.hibernate.id.enhanced.TableStructure : could not read a hi value
net.snowflake.client.jdbc.SnowflakeSQLException: SQL compilation error:
syntax error line 1 at position 50 unexpected 'with'.
syntax error line 1 at position 72 unexpected ')'.
at net.snowflake.client.jdbc.SnowflakeUtil.checkErrorAndThrowExceptionSub(SnowflakeUtil.java:124)
at net.snowflake.client.jdbc.SnowflakeUtil.checkErrorAndThrowException(SnowflakeUtil.java:64)
at net.snowflake.client.core.StmtUtil.pollForOutput(StmtUtil.java:434)
at net.snowflake.client.core.StmtUtil.execute(StmtUtil.java:338)
at net.snowflake.client.core.SFStatement.executeHelper(SFStatement.java:506)
at net.snowflake.client.core.SFStatement.executeQueryInternal(SFStatement.java:233)
at net.snowflake.client.core.SFStatement.executeQuery(SFStatement.java:171)
at net.snowflake.client.core.SFStatement.execute(SFStatement.java:754)
at net.snowflake.client.jdbc.SnowflakeStatementV1.executeQueryInternal(SnowflakeStatementV1.java:245)
at net.snowflake.client.jdbc.SnowflakePreparedStatementV1.executeQuery(SnowflakePreparedStatementV1.java:117)
Just as a follow up, I was unable to get the application up and running using the approach I outlined above. I am still unsure why but think it may have been to do with a lack of support for snowflake sequences as the generation type for the primary key in spring.
I changed the generation type to UUID and the application started to work as expected in turn. There was no requirements for what type of primary key was needed so this approach was satisfactory.
create sequence award_event_id_seq;
create table award_event
(
id varchar not null constraint award_event_pkey primary key,
event_source_system varchar not null,
event_trigger varchar not null,
event_triggered_by varchar not null,
event_timestamp timestamp not null
)
#Entity(name = "award_event")
data class AwardEvent(
#Id
#GeneratedValue
#Type(type = "uuid-char")
val id: UUID = UUID.randomUUID(),
val eventTrigger: String,
val eventTriggeredBy: String,
val eventTimestamp: LocalDateTime,
val eventSourceSystem: String
)
When I create a new Dev Express XAF application using the wizard 20.1.3 for .netcore3.1 the code works fine. I can enable migrations and run a migration without problems. (Or so I thought ... see below)
However for certain reasons (my legacy call run-migrations code) I want to provide the connection string location to the constructor
When I do this, and try to add a migratiion I get an error
The DbContext is set up as
using System;
using System.Data.Entity;
using System.Data.Common;
using DevExpress.ExpressApp.EF.Updating;
using DevExpress.Persistent.BaseImpl.EF;
using DevExpress.ExpressApp.Design;
using DevExpress.Persistent.BaseImpl.EF.PermissionPolicy;
namespace Creatures.Module.BusinessObjects {
[TypesInfoInitializer(typeof(CreaturesContextInitializer))]
public class CreaturesDbContext : DbContext {
public CreaturesDbContext(String connectionString)
: base(connectionString) {
}
public CreaturesDbContext(DbConnection connection)
: base(connection, false) {
}
// migrations work
public CreaturesDbContext()
{
}
// migratations do not work
//public CreaturesDbContext()
// : base("name=ConnectionString")
// {
//
// }
public DbSet<ModuleInfo> ModulesInfo { get; set; }
public DbSet<PermissionPolicyRole> Roles { get; set; }
public DbSet<PermissionPolicyTypePermissionObject> TypePermissionObjects { get; set; }
public DbSet<PermissionPolicyUser> Users { get; set; }
public DbSet<ModelDifference> ModelDifferences { get; set; }
public DbSet<ModelDifferenceAspect> ModelDifferenceAspects { get; set; }
public DbSet<Cat> Cats { get; set; }
}
}
Also there is
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using DevExpress.ExpressApp.EF.DesignTime;
namespace Creatures.Module.BusinessObjects
{
public class CreaturesContextInitializer : DbContextTypesInfoInitializerBase {
protected override DbContext CreateDbContext() {
DbContextInfo contextInfo = new DbContextInfo(typeof(CreaturesDbContext), new DbProviderInfo(providerInvariantName: "System.Data.SqlClient", providerManifestToken: "2008"));
return contextInfo.CreateInstance();
}
}
}
In the win project app.config i have
<add name="ConnectionString" providerName="System.Data.SqlClient" connectionString="Server=myserver;Database=Creatures;Integrated Security=false;MultipleActiveResultSets=True;user=myuser;pwd=mypassword;" />
At PM Console I add a migration
PM> add-migration migration-name
output is
System.InvalidOperationException: No connection string named 'ConnectionString' could be found in the application config file.
at System.Data.Entity.Internal.LazyInternalConnection.Initialize()
at System.Data.Entity.Internal.LazyInternalConnection.get_Connection()
at System.Data.Entity.Internal.LazyInternalContext.get_Connection()
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbProviderInfo modelProviderInfo, AppConfig config, DbConnectionInfo connectionInfo, Func`1 resolver)
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, Func`1 resolver)
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext, DatabaseExistenceState existenceState, Boolean calledByCreateDatabase)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
at System.Data.Entity.Migrations.Design.MigrationScaffolder..ctor(DbMigrationsConfiguration migrationsConfiguration)
at System.Data.Entity.Infrastructure.Design.Executor.CreateMigrationScaffolder(DbMigrationsConfiguration configuration)
at System.Data.Entity.Infrastructure.Design.Executor.ScaffoldInternal(String name, DbConnectionInfo connectionInfo, String migrationsConfigurationName, Boolean ignoreChanges)
at System.Data.Entity.Infrastructure.Design.Executor.Scaffold.<>c__DisplayClass0_0.<.ctor>b__0()
at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.<>c__DisplayClass4_0`1.<Execute>b__0()
at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.Execute(Action action)
No connection string named 'ConnectionString' could be found in the application config file.
I also wonder how the constructor "knows" the connection string location when it is called without inheriting from base
[Update]
I tried to make a work around but could not get it working
The source is available on GitHub
I can use Package Manager Console to create migrations in the code.
But if I try to update the database from PM it creates a new database with the wrong name.
The gist of my hack is the following
public static void RunMigrations(Creatures3DbContext db)
{
var configuration = new Configuration();
var migrator = new DbMigrator(configuration);
var pendings = migrator.GetPendingMigrations(); // gets the migrations if only if it is not told the db
var migratorwithDb = new DbMigrator(configuration, db); // runs the migrations only if it is told the db
foreach (var pending in pendings)
{
migratorwithDb.Update(pending); // appears to run but the application still has a model backing failure
}
}
[Update]
I can enable migrations and run a migration without problems.
Or so I thought. When I reinvestigated this it turns out that the migration is creating a different database.
Here is the start of the output from PM
PM> enable-migrations
Checking if the context targets an existing database...
PM> add-migration cat
Scaffolding migration 'cat'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration cat' again.
PM> update-database -verbose
C:\Program Files\dotnet\dotnet.exe exec --depsfile D:\Users\kirst\source\repos\Creatures6\Creatures6.Module\bin\Debug\netcoreapp3.0\Creatures6.Module.deps.json --additionalprobingpath C:\Users\kirst\.nuget\packages --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig D:\Users\kirst\source\repos\Creatures6\Creatures6.Module\bin\Debug\netcoreapp3.0\Creatures6.Module.runtimeconfig.json C:\Users\kirst\.nuget\packages\entityframework\6.4.0\tools\netcoreapp3.0\any\ef6.dll database update --verbose --no-color --prefix-output --assembly D:\Users\kirst\source\repos\Creatures6\Creatures6.Module\bin\Debug\netcoreapp3.0\Creatures6.Module.dll --project-dir D:\Users\kirst\source\repos\Creatures6\Creatures6.Module\ --language C# --root-namespace Creatures6.Module --config D:\Users\kirst\source\repos\Creatures6\Creatures6.Win\App.config
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Target database is: 'Creatures6.Module.BusinessObjects.Creatures6DbContext' (DataSource: (localdb)\mssqllocaldb, Provider: System.Data.SqlClient, Origin: Convention).
Applying explicit migrations: [202005150136061_cat].
Applying explicit migration: 202005150136061_cat.
CREATE TABLE [dbo].[ModelDifferenceAspects] (
[ID] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[Xml] [nvarchar](max),
[Owner_ID] [int],
CONSTRAINT [PK_dbo.ModelDifferenceAspects] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_Owner_ID] ON [dbo].[ModelDifferenceAspects]([Owner_ID])
CREATE TABLE [dbo].[ModelDifferences] (
[ID] [int] NOT NULL IDENTITY,
[UserId] [nvarchar](max),
[ContextId] [nvarchar](max),
[Version] [int] NOT NULL,
CONSTRAINT [PK_dbo.ModelDifferences] PRIMARY KEY ([ID])
)
CREATE TABLE [dbo].[ModuleInfoes] (
[ID] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[Version] [nvarchar](max),
[AssemblyFileName] [nvarchar](max),
[IsMain] [bit] NOT NULL,
CONSTRAINT [PK_dbo.ModuleInfoes] PRIMARY KEY ([ID])
)
CREATE TABLE [dbo].[PermissionPolicyRoleBases] (
[ID] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[IsAdministrative] [bit] NOT NULL,
[CanEditModel] [bit] NOT NULL,
[PermissionPolicy] [int] NOT NULL,
[IsAllowPermissionPriority] [bit] NOT NULL,
[Discriminator] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_dbo.PermissionPolicyRoleBases] PRIMARY KEY ([ID])
)
CREATE TABLE [dbo].[PermissionPolicyActionPermissionObjects] (
[ID] [int] NOT NULL IDENTITY,
[ActionId] [nvarchar](max),
[Role_ID] [int],
CONSTRAINT [PK_dbo.PermissionPolicyActionPermissionObjects] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_Role_ID] ON [dbo].[PermissionPolicyActionPermissionObjects]([Role_ID])
CREATE TABLE [dbo].[PermissionPolicyNavigationPermissionObjects] (
[ID] [int] NOT NULL IDENTITY,
[ItemPath] [nvarchar](max),
[TargetTypeFullName] [nvarchar](max),
[NavigateState] [int],
[Role_ID] [int],
CONSTRAINT [PK_dbo.PermissionPolicyNavigationPermissionObjects] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_Role_ID] ON [dbo].[PermissionPolicyNavigationPermissionObjects]([Role_ID])
CREATE TABLE [dbo].[PermissionPolicyTypePermissionObjects] (
[ID] [int] NOT NULL IDENTITY,
[TargetTypeFullName] [nvarchar](max),
[ReadState] [int],
[WriteState] [int],
[CreateState] [int],
[DeleteState] [int],
[NavigateState] [int],
[Role_ID] [int],
CONSTRAINT [PK_dbo.PermissionPolicyTypePermissionObjects] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_Role_ID] ON [dbo].[PermissionPolicyTypePermissionObjects]([Role_ID])
CREATE TABLE [dbo].[PermissionPolicyMemberPermissionsObjects] (
[ID] [int] NOT NULL IDENTITY,
[Members] [nvarchar](max),
[Criteria] [nvarchar](max),
[ReadState] [int],
[WriteState] [int],
[TypePermissionObject_ID] [int],
CONSTRAINT [PK_dbo.PermissionPolicyMemberPermissionsObjects] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_TypePermissionObject_ID] ON [dbo].[PermissionPolicyMemberPermissionsObjects]([TypePermissionObject_ID])
CREATE TABLE [dbo].[PermissionPolicyObjectPermissionsObjects] (
[ID] [int] NOT NULL IDENTITY,
[Criteria] [nvarchar](max),
[ReadState] [int],
[WriteState] [int],
[DeleteState] [int],
[NavigateState] [int],
[TypePermissionObject_ID] [int],
CONSTRAINT [PK_dbo.PermissionPolicyObjectPermissionsObjects] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_TypePermissionObject_ID] ON [dbo].[PermissionPolicyObjectPermissionsObjects]([TypePermissionObject_ID])
CREATE TABLE [dbo].[PermissionPolicyUsers] (
[ID] [int] NOT NULL IDENTITY,
[UserName] [nvarchar](max),
[IsActive] [bit] NOT NULL,
[ChangePasswordOnFirstLogon] [bit] NOT NULL,
[StoredPassword] [nvarchar](max),
CONSTRAINT [PK_dbo.PermissionPolicyUsers] PRIMARY KEY ([ID])
)
CREATE TABLE [dbo].[PermissionPolicyUserPermissionPolicyRoles] (
[PermissionPolicyUser_ID] [int] NOT NULL,
[PermissionPolicyRole_ID] [int] NOT NULL,
CONSTRAINT [PK_dbo.PermissionPolicyUserPermissionPolicyRoles] PRIMARY KEY ([PermissionPolicyUser_ID], [PermissionPolicyRole_ID])
)
CREATE INDEX [IX_PermissionPolicyUser_ID] ON [dbo].[PermissionPolicyUserPermissionPolicyRoles]([PermissionPolicyUser_ID])
CREATE INDEX [IX_PermissionPolicyRole_ID] ON [dbo].[PermissionPolicyUserPermissionPolicyRoles]([PermissionPolicyRole_ID])
ALTER TABLE [dbo].[ModelDifferenceAspects] ADD CONSTRAINT [FK_dbo.ModelDifferenceAspects_dbo.ModelDifferences_Owner_ID] FOREIGN KEY ([Owner_ID]) REFERENCES [dbo].[ModelDifferences] ([ID])
ALTER TABLE [dbo].[PermissionPolicyActionPermissionObjects] ADD CONSTRAINT [FK_dbo.PermissionPolicyActionPermissionObjects_dbo.PermissionPolicyRoleBases_Role_ID] FOREIGN KEY ([Role_ID]) REFERENCES [dbo].[PermissionPolicyRoleBases] ([ID])
ALTER TABLE [dbo].[PermissionPolicyNavigationPermissionObjects] ADD CONSTRAINT [FK_dbo.PermissionPolicyNavigationPermissionObjects_dbo.PermissionPolicyRoleBases_Role_ID] FOREIGN KEY ([Role_ID]) REFERENCES [dbo].[PermissionPolicyRoleBases] ([ID])
ALTER TABLE [dbo].[PermissionPolicyTypePermissionObjects] ADD CONSTRAINT [FK_dbo.PermissionPolicyTypePermissionObjects_dbo.PermissionPolicyRoleBases_Role_ID] FOREIGN KEY ([Role_ID]) REFERENCES [dbo].[PermissionPolicyRoleBases] ([ID])
ALTER TABLE [dbo].[PermissionPolicyMemberPermissionsObjects] ADD CONSTRAINT [FK_dbo.PermissionPolicyMemberPermissionsObjects_dbo.PermissionPolicyTypePermissionObjects_TypePermissionObject_ID] FOREIGN KEY ([TypePermissionObject_ID]) REFERENCES [dbo].[PermissionPolicyTypePermissionObjects] ([ID])
ALTER TABLE [dbo].[PermissionPolicyObjectPermissionsObjects] ADD CONSTRAINT [FK_dbo.PermissionPolicyObjectPermissionsObjects_dbo.PermissionPolicyTypePermissionObjects_TypePermissionObject_ID] FOREIGN KEY ([TypePermissionObject_ID]) REFERENCES [dbo].[PermissionPolicyTypePermissionObjects] ([ID])
ALTER TABLE [dbo].[PermissionPolicyUserPermissionPolicyRoles] ADD CONSTRAINT [FK_dbo.PermissionPolicyUserPermissionPolicyRoles_dbo.PermissionPolicyUsers_PermissionPolicyUser_ID] FOREIGN KEY ([PermissionPolicyUser_ID]) REFERENCES [dbo].[PermissionPolicyUsers] ([ID]) ON DELETE CASCADE
ALTER TABLE [dbo].[PermissionPolicyUserPermissionPolicyRoles] ADD CONSTRAINT [FK_dbo.PermissionPolicyUserPermissionPolicyRoles_dbo.PermissionPolicyRoleBases_PermissionPolicyRole_ID] FOREIGN KEY ([PermissionPolicyRole_ID]) REFERENCES [dbo].[PermissionPolicyRoleBases] ([ID]) ON DELETE CASCADE
CREATE TABLE [dbo].[__MigrationHistory] (
[MigrationId] [nvarchar](150) NOT NULL,
[ContextKey] [nvarchar](300) NOT NULL,
[Model] [varbinary](max) NOT NULL,
[ProductVersion] [nvarchar](32) NOT NULL,
CONSTRAINT [PK_dbo.__MigrationHistory] PRIMARY KEY ([MigrationId], [ContextKey])
)
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES (N'202005150136061_cat', N'Creatures6.Module.Migrations.Configuration', etc
[Update]
I tried making some new xaf projects for 20.1.3 Framework and 19.2 Framework
They both had issues locating the database when I try to run Update-Database -verbose from Package Manager Console. The output indicated
Target database is: 'creatures9.Module.BusinessObjects.creatures9DbContext' (DataSource: .\SQLEXPRESS, Provider: System.Data.SqlClient, Origin: Convention).
I am running VS2019 16.5.4
The connection string in app.config does not mention SQLExpress and the database name is creatures9
I don't think the problem is Entity Framwork itself because XAF 19.2 use EF 6.2
I am now thinking something in Nuget maybe.
I just updated to VS 2019 16.5.5 but it did not help.
[Update]
[Update]
Maybe I need to specify the location of the connection string when I enable migrations
I tried
enable-migrations -StartupProjectName Creatures3.Win -ConnectionStringName ConnectionString
However that comes up with a message
No connection string named 'ConnectionString' could be found in the application config file.
It seems that if I use
update-database
without specifying the connectionstring location then it creates and updates a database with the full name of the context
I want it to use the database specified in the connection string so I tried
update-database -verbose -StartupProjectName Creatures3.Win -ConnectionStringName ConnectionString
The output was as follows
C:\Program Files\dotnet\dotnet.exe exec --depsfile D:\Users\kirst\source\repos\Creatures3\Creatures3.Module\bin\Debug\netcoreapp3.0\Creatures3.Module.deps.json --additionalprobingpath C:\Users\kirst\.nuget\packages --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig D:\Users\kirst\source\repos\Creatures3\Creatures3.Module\bin\Debug\netcoreapp3.0\Creatures3.Module.runtimeconfig.json C:\Users\kirst\.nuget\packages\entityframework\6.4.0\tools\netcoreapp3.0\any\ef6.dll database update --connection-string-name ConnectionString --verbose --no-color --prefix-output --assembly D:\Users\kirst\source\repos\Creatures3\Creatures3.Module\bin\Debug\netcoreapp3.0\Creatures3.Module.dll --project-dir D:\Users\kirst\source\repos\Creatures3\Creatures3.Module\ --language C# --root-namespace Creatures3.Module --config D:\Users\kirst\source\repos\Creatures3\Creatures3.Win\App.config
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
System.InvalidOperationException: No connection string named 'ConnectionString' could be found in the application config file.
at System.Data.Entity.Infrastructure.DbConnectionInfo.GetConnectionString(AppConfig config)
at System.Data.Entity.Internal.LazyInternalConnection.get_ConnectionHasModel()
at System.Data.Entity.Internal.LazyInternalContext.OverrideConnection(IInternalConnection connection)
at System.Data.Entity.Infrastructure.DbContextInfo.ConfigureContext(DbContext context)
at System.Data.Entity.Internal.InternalContext.ApplyContextInfo(DbContextInfo info)
at System.Data.Entity.Infrastructure.DbContextInfo.CreateInstance()
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbProviderInfo modelProviderInfo, AppConfig config, DbConnectionInfo connectionInfo, Func`1 resolver)
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbConnectionInfo connectionInfo)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext, DatabaseExistenceState existenceState, Boolean calledByCreateDatabase)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
at System.Data.Entity.Infrastructure.Design.Executor.CreateMigrator(DbMigrationsConfiguration configuration)
at System.Data.Entity.Infrastructure.Design.Executor.UpdateInternal(String targetMigration, Boolean force, DbConnectionInfo connectionInfo, String migrationsConfigurationName)
at System.Data.Entity.Infrastructure.Design.Executor.Update.<>c__DisplayClass0_0.<.ctor>b__0()
at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.Execute(Action action)
No connection string named 'ConnectionString' could be found in the application config file.
I thing can work around things by following this crazy procedure
After creating the initial program from the XAF Wizard run the program to create the database.
Then enable-migrations
Then add-migration one
Then update-database
Then add the code to run migrations in the project.
and comment out the intial code to create tables in migration one
Then run the project
Then for each new migration create it using add-migration
Run update-database to update the incorrect database created
Run the code itself to update the correct database.
I tried specifying the connection string location when adding the migration
PM> add-migration one -StartupProjectName Creatures3.Win -ConnectionStringName ConnectionString
But I got
System.InvalidOperationException: No connection string named 'ConnectionString' could be found in the application config file.
at System.Data.Entity.Infrastructure.DbConnectionInfo.GetConnectionString(AppConfig config)
at System.Data.Entity.Internal.LazyInternalConnection.get_ConnectionHasModel()
at System.Data.Entity.Internal.LazyInternalContext.OverrideConnection(IInternalConnection connection)
at System.Data.Entity.Infrastructure.DbContextInfo.ConfigureContext(DbContext context)
at System.Data.Entity.Internal.InternalContext.ApplyContextInfo(DbContextInfo info)
at System.Data.Entity.Infrastructure.DbContextInfo.CreateInstance()
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbProviderInfo modelProviderInfo, AppConfig config, DbConnectionInfo connectionInfo, Func`1 resolver)
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbConnectionInfo connectionInfo)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext, DatabaseExistenceState existenceState, Boolean calledByCreateDatabase)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
at System.Data.Entity.Migrations.Design.MigrationScaffolder..ctor(DbMigrationsConfiguration migrationsConfiguration)
at System.Data.Entity.Infrastructure.Design.Executor.CreateMigrationScaffolder(DbMigrationsConfiguration configuration)
at System.Data.Entity.Infrastructure.Design.Executor.ScaffoldInternal(String name, DbConnectionInfo connectionInfo, String migrationsConfigurationName, Boolean ignoreChanges)
at System.Data.Entity.Infrastructure.Design.Executor.Scaffold.<>c__DisplayClass0_0.<.ctor>b__0()
at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.<>c__DisplayClass4_0`1.<Execute>b__0()
at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.Execute(Action action)
No connection string named 'ConnectionString' could be found in the application config file.
[Update]
I tried the using the connection string itself when calling update-database
update-database -connectionString "Integrated Security=SSPI;MultipleActiveResultSets=True;Data Source=(localdb)\mssqllocaldb;Initial Catalog=Creatures3F"
It asked me for the connection provider name
so typed in
"System.Data.SqlClient"
So it seems thatthe following is a work around
update-database -connectionString "Integrated Security=SSPI;MultipleActiveResultSets=True;Data Source=(localdb)\mssqllocaldb;Initial Catalog=Creatures3" -ConnectionProviderName "System.Data.SqlClient" -verbose
[Update]
I tried Kahbazi's suggestion but there is an error creating the migration.
PM> add-migration E
gives
System.InvalidOperationException: No connection string named 'ConnectionString' could be found in the application config file.
at System.Data.Entity.Internal.LazyInternalConnection.Initialize()
at System.Data.Entity.Internal.LazyInternalConnection.get_Connection()
at System.Data.Entity.Internal.LazyInternalContext.get_Connection()
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, DbProviderInfo modelProviderInfo, AppConfig config, DbConnectionInfo connectionInfo, Func`1 resolver)
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType, Func`1 resolver)
at System.Data.Entity.Infrastructure.DbContextInfo..ctor(Type contextType)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext, DatabaseExistenceState existenceState, Boolean calledByCreateDatabase)
at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
at System.Data.Entity.Migrations.Design.MigrationScaffolder..ctor(DbMigrationsConfiguration migrationsConfiguration)
at System.Data.Entity.Infrastructure.Design.Executor.CreateMigrationScaffolder(DbMigrationsConfiguration configuration)
at System.Data.Entity.Infrastructure.Design.Executor.ScaffoldInternal(String name, DbConnectionInfo connectionInfo, String migrationsConfigurationName, Boolean ignoreChanges)
at System.Data.Entity.Infrastructure.Design.Executor.Scaffold.<>c__DisplayClass0_0.<.ctor>b__0()
at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.<>c__DisplayClass4_0`1.<Execute>b__0()
at System.Data.Entity.Infrastructure.Design.Executor.OperationBase.Execute(Action action)
No connection string named 'ConnectionString' could be found in the application config file.
[Update]
I tried Kahbazi's suggestion to use CreaturesDbContextFactory
but
update-database -vebose
shows target database as 'Creatures3.Module.BusinessObjects.Creatures3DbContext'
You can implement IDbContextFactory and hardcode your connection string in it, so the migrations command could use it.
public class CreaturesDbContextFactory : IDbContextFactory<CreaturesDbContext>
{
public CreaturesDbContext Create()
{
return new CreaturesDbContext("Integrated Security=SSPI;MultipleActiveResultSets=True;Data Source=(localdb)\mssqllocaldb;Initial Catalog=Creatures3");
}
}
Also in your DbContext you must have a constructor which takes connection string.
public CreaturesDbContext(string connectionString)
: base(connectionString)
{
...
}
Let's say you have this constructor
public CreaturesDbContext()
: base("name=MyDatabaseConnectionString")
{
...
}
When you are running update-database command, it will look into your app.config or web.config (based on what type of project your DbContext exists in) and look for a connection string with the name of MyDatabaseConnectionString.
The error you are getting is simply because there is no connection string with the name of MyDatabaseConnectionString in your app.config/web.config.
Add this in your app.config/web.config to fix this issue, so you don't need to send the connection string with updata-databse command everytime.
<configuration>
<connectionStrings>
<add name="MyDatabaseConnectionString" connectionString="Integrated Security=SSPI;MultipleActiveResultSets=True;Data Source=(localdb)\mssqllocaldb;Initial Catalog=Creatures3" />
</connectionStrings>
</configuration>
It seems that this is a bug in EF6.4 with DotNetCore and the work around is to provide the connection string etc to the update-database command as at the end of my question.
I've started to get this issue when I switched my data project to from .NET4.7 to .NET Core 3.1 framework.
So in my .csproj I've replaced
<TargetFramework>netcoreapp3.1</TargetFramework>
By
<TargetFrameworks>net47;netcoreapp3.1</TargetFrameworks>
The the migration commands restarted to work properly. The downside is it's now a multi framework project :)
I've also noticed the order of the frameworks is important ("netcoreapp3.1;net47" failed)
Note: I'm also using EF version 6.4
I am trying to use Spring Data JDBC for my PostgreSQL database. I defined the following beans
#Data
class Report {
#Id
private Long id;
private String name;
private Set<Dimension> dimensions;
}
#Data
class Dimension {
private String name;
private Long[] filterIds;
}
and the corresponding DDL
CREATE TABLE report (
id bigserial PRIMARY KEY,
name text NOT NULL
);
CREATE TABLE dimension (
id bigserial PRIMARY KEY ,
report bigint,
name text,
filter_ids bigint[],
FOREIGN KEY (report) REFERENCES report(id) ON DELETE CASCADE ON UPDATE CASCADE
);
Then I tried to insert a report
final Dimension dimension = new Dimension();
dimension.setName("xyz");
dimension.setFilterIds(new Long[]{ 1L, 2L, 3L });
final Report report = new Report();
report.setName("xyz");
report.setDimensions(Collections.singleton(dimension));
repository.save(report);
where repository is simply a CrudRepository<Report, Long>.
This gave me the following error
org.postgresql.util.PSQLException: ERROR: column "filter_ids" is of type bigint[] but expression is of type bigint
Hinweis: You will need to rewrite or cast the expression.
Position: 116
Can I somehow tell Spring Data JDBC how to map the array types?
With the release of Spring Data JDBC 1.1.0, this became possible. See the documentation here:
The properties of the following types are currently supported:
All primitive types and their boxed types (int, float, Integer, Float, and so on)
Enums get mapped to their name.
String
java.util.Date, java.time.LocalDate, java.time.LocalDateTime, and java.time.LocalTime
Arrays and Collections of the types mentioned above can be mapped to columns of array type if your database supports that.
...
As P44T answered this should work from version of 1.1 of Spring Data JDBC onwards just as you used it.
Original answer
It is currently not possible. There are issues for this. A starting point is this one: https://jira.spring.io/browse/DATAJDBC-259
I'm trying the following unit test:
#Test
#Transactional
public void thatFolderLocationAssociationTableIsWorking() {
Location l1 = new DomesticLocation();
l1.setDeptName("Test name 1");
Location l2 = new DomesticLocation();
l2.setDeptName("Test name 2");
KMLFolder k1 = new KMLFolder();
k1.setName("Test name 1");
KMLFolder k2 = new KMLFolder();
k1.setName("Test name 2");
List<Location> locations = new ArrayList<Location>();
locations.add(l1);
locations.add(l2);
k1.setLocations(locations);
kmlFolderServiceImpl.save(k1);
assertEquals("Test name 1", kmlFolderServiceImpl.find(1L).getLocations().get(0).getDeptName());
assertEquals("Test name 2", kmlFolderServiceImpl.find(1L).getLocations().get(1).getDeptName());
//The following line gets the NPE
assertEquals("Test name 1", locationServiceImpl.find(1L).getKmlFolderList().get(0).getName());
}
I'm getting NPEs on the laster assertions where I'm trying to retrieve KMLFolder.getName() from the Locations The other assertions are working, where I get the Location name from the KMLFolder.
Here are my JPA definitions:
#ManyToMany(mappedBy="kmlFolderList", cascade=CascadeType.ALL)
private List<Location> locations = new ArrayList<Location>();
#ManyToMany
#JoinTable(name="LOCATION_KMLFOLDER",
joinColumns={#JoinColumn(name="KMLFOLDER_ID", referencedColumnName="ID")},
inverseJoinColumns={#JoinColumn(name="LOCATION_ID", referencedColumnName="ID")}
)
The appropriate table is being created when I run the test. Here's the console output:
Hibernate:
create table project.LOCATION_KMLFOLDER (
KMLFOLDER_ID bigint not null,
LOCATION_ID bigint not null
) ENGINE=InnoDB
...
Hibernate:
alter table project.LOCATION_KMLFOLDER
add index FK_lqllrwb2t5cn0cbxxx3ms26ku (LOCATION_ID),
add constraint FK_lqllrwb2t5cn0cbxxx3ms26ku
foreign key (LOCATION_ID)
references project.KMLFolder (id)
Hibernate:
alter table .LOCATION_KMLFOLDER
add index FK_ckj00nos13yojmcyvtefgk9pl (KMLFOLDER_ID),
add constraint FK_ckj00nos13yojmcyvtefgk9pl
foreign key (KMLFOLDER_ID)
references project.Locations (id)
The console does not show inserts in to the LOCATION_KNLFOLDER table as I expect. Any thoughts on why this may be happening?
You're initializing the inverse side of the association, that Hibernate ignores, instead of (or in addition to) initializing the owner side of the association, that Hibernate doesn't ignore.
The owner side is the side without the mappedBy attribute.
Below is the code I am using to save a record in the database and then get the generated primary key.
public void save(User user) {
// TODO Auto-generated method stub
Object[] args = { user.getFirstname(), user.getLastname(),
user.getEmail() };
int[] types = { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR };
SqlUpdate su = new SqlUpdate();
su.setJdbcTemplate(getJdbcTemplate());
su.setSql(QUERY_SAVE);
setSqlTypes(su, types);
su.setReturnGeneratedKeys(true);
su.compile();
KeyHolder keyHolder = new GeneratedKeyHolder();
su.update(args, keyHolder);
int id = keyHolder.getKey().intValue();
if (su.isReturnGeneratedKeys()) {
user.setId(id);
} else {
throw new RuntimeException("No key generated for insert statement");
}
}
But its not working, It gives me following error.
The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number]
The row is being inserted in the database properly. As well I could get the generataed primary key when using MS SQL database but the same code is not working with the ORACLE 11G.
Please help.
As in the comment, oracle rowid's are alpha numerical so can't be cast to an int.
Besides that, you should not use the generated rowid anywhere in your code. This is not the primary key that you defined on the table.
MS SQL has the option to declare a column as a primary key which auto-increments. This is a functionality that does not work in oracle.
What I always do (regardless if the db supports auto-increment) is the following:
select sequenceName.nextval from dual
The value returned by the previous statement is used as the primary key for the insert statement.
insert into something (pk, ...) values (:pk,:.....)
That way we always have the pk after the insert.