Today I migrate my project to .NET 6 with MySql db.
I tried Pomelo.EntityFrameworkCore.MySql for the first time but several errors had occur.
I fixed some of them but the last one, I couldn`t.
System.InvalidOperationException: The property 'SqlClass.Disabled' is of type 'byte' which is not supported by the current database provider. Either change the property CLR type, or ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
entity.Property(e => e.Disabled)
//.HasConversion<int>()
.IsRequired()
.HasMaxLength(255)
.HasColumnName("disabled")
.HasColumnType("tinyint(1)");
[Required]
[MaxLength(255)]
public byte Disabled { get; set; }
Any ideas ? Thank you.
There are basically 3 simple options, all demonstrated here with the IceCream.Available property:
1. Use System.Boolean instead of System.Byte
Pomelo translates tinyint(1) to System.Boolean by default. So if you change the CLR type of your property from byte to bool, it works out-of-the box:
Program.cs
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
[Required]
[Column(TypeName = "tinyint(1)")] // <-- redundant (bool is translated to tinyint(1) by default)
public bool Available { get; set; } // <-- use bool
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So70198786";
var serverVersion = ServerVersion.AutoDetect(connectionString);
optionsBuilder
.UseMySql(connectionString, serverVersion)
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCream>(
entity =>
{
// Not needed if you are using data annotations:
//
// entity.Property(e => e.Available)
// .IsRequired()
// .HasColumnType("tinyint(1)"); // <-- redundant (bool is translated to tinyint(1) by default)
entity.HasData(
new IceCream
{
IceCreamId = 1,
Name = "Vanilla",
Available = true, // <-- bool
},
new IceCream
{
IceCreamId = 2,
Name = "Chocolate",
Available = false, // <-- bool
});
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var availableIceCreams = context.IceCreams
.Where(i => i.Available) // <-- bool
.ToList();
Trace.Assert(availableIceCreams.Count == 1);
Trace.Assert(availableIceCreams[0].Name == "Vanilla");
}
}
}
Output (SQL)
warn: Microsoft.EntityFrameworkCore.Model.Validation[10400]
Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development.
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 6.0.0 initialized 'Context' using provider 'Pomelo.EntityFrameworkCore.MySql:6.0.0-rtm.1' with options: ServerVersion 8.0.25-mysql SensitiveDataLoggingEnabled DetailedErrorsEnabled
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (38ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
DROP DATABASE `So70198786`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (13ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE DATABASE `So70198786`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (15ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER DATABASE CHARACTER SET utf8mb4;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (47ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE `IceCreams` (
`IceCreamId` int NOT NULL AUTO_INCREMENT,
`Name` longtext CHARACTER SET utf8mb4 NULL,
`Available` tinyint(1) NOT NULL,
CONSTRAINT `PK_IceCreams` PRIMARY KEY (`IceCreamId`)
) CHARACTER SET=utf8mb4;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`IceCreamId`, `Available`, `Name`)
VALUES (1, TRUE, 'Vanilla');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`IceCreamId`, `Available`, `Name`)
VALUES (2, FALSE, 'Chocolate');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `i`.`IceCreamId`, `i`.`Available`, `i`.`Name`
FROM `IceCreams` AS `i`
WHERE `i`.`Available`
2. Use tinyint instead of tinyint(1)
In case you definitely want to use System.Byte as the CLR type of your property, use tinyint instead of tinyint(1). All tinyint except tinyint(1) are translated to System.Byte by default:
Program.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
[Required]
[Column(TypeName = "tinyint")] // <-- redundant (byte is translated to tinyint by default)
public byte Available { get; set; }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So70198786_01";
var serverVersion = ServerVersion.AutoDetect(connectionString);
optionsBuilder
.UseMySql(connectionString, serverVersion)
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCream>(
entity =>
{
// Not needed if you are using data annotations:
//
// entity.Property(e => e.Available)
// .IsRequired()
// .HasColumnType("tinyint"); // <-- redundant (byte is translated to tinyint by default)
entity.HasData(
new IceCream
{
IceCreamId = 1,
Name = "Vanilla",
Available = 1, // <-- byte
},
new IceCream
{
IceCreamId = 2,
Name = "Chocolate",
Available = 0, // <-- byte
});
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var availableIceCreams = context.IceCreams
.Where(i => i.Available != 0) // <-- byte
.ToList();
Trace.Assert(availableIceCreams.Count == 1);
Trace.Assert(availableIceCreams[0].Name == "Vanilla");
}
}
}
Output (SQL)
warn: Microsoft.EntityFrameworkCore.Model.Validation[10400]
Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development.
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 6.0.0 initialized 'Context' using provider 'Pomelo.EntityFrameworkCore.MySql:6.0.0-rtm.1' with options: ServerVersion 8.0.25-mysql SensitiveDataLoggingEnabled DetailedErrorsEnabled
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (42ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
DROP DATABASE `So70198786_01`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (41ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE DATABASE `So70198786_01`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (19ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER DATABASE CHARACTER SET utf8mb4;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (48ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE `IceCreams` (
`IceCreamId` int NOT NULL AUTO_INCREMENT,
`Name` longtext CHARACTER SET utf8mb4 NULL,
`Available` tinyint NOT NULL,
CONSTRAINT `PK_IceCreams` PRIMARY KEY (`IceCreamId`)
) CHARACTER SET=utf8mb4;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`IceCreamId`, `Available`, `Name`)
VALUES (1, 1, 'Vanilla');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`IceCreamId`, `Available`, `Name`)
VALUES (2, 0, 'Chocolate');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (6ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `i`.`IceCreamId`, `i`.`Available`, `i`.`Name`
FROM `IceCreams` AS `i`
WHERE `i`.`Available` <> 0
3. Change default System.Boolean mapping to bit(1) or remove it altogether
Finally, if you need to map exactly tinyint(1) to System.Byte, then you can change the default mapping that Pomelo is using for System.Boolean by setting a DbContext option in your UseMySql() call:
Program.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
[Required]
[Column(TypeName = "tinyint(1)")] // <-- necessary (otherwise gets translated to tinyint)
public byte Available { get; set; }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So70198786_02";
var serverVersion = ServerVersion.AutoDetect(connectionString);
optionsBuilder
.UseMySql(
connectionString,
serverVersion,
options => options.DefaultDataTypeMappings( // <-- change default data type mappings
m => m.WithClrBoolean(MySqlBooleanType.Bit1))) // <-- or even MySqlBooleanType.None
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCream>(
entity =>
{
// Not needed if you are using data annotations:
//
// entity.Property(e => e.Available)
// .IsRequired()
// .HasColumnType("tinyint(1)"); // <-- necessary (otherwise gets translated to tinyint)
entity.HasData(
new IceCream
{
IceCreamId = 1,
Name = "Vanilla",
Available = 1, // <-- byte
},
new IceCream
{
IceCreamId = 2,
Name = "Chocolate",
Available = 0, // <-- byte
});
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var availableIceCreams = context.IceCreams
.Where(i => i.Available != 0) // <-- byte
.ToList();
Trace.Assert(availableIceCreams.Count == 1);
Trace.Assert(availableIceCreams[0].Name == "Vanilla");
}
}
}
Output (SQL)
warn: Microsoft.EntityFrameworkCore.Model.Validation[10400]
Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development.
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 6.0.0 initialized 'Context' using provider 'Pomelo.EntityFrameworkCore.MySql:6.0.0-rtm.1' with options: ServerVersion 8.0.25-mysql SensitiveDataLoggingEnabled DetailedErrorsEnabled
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (40ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
DROP DATABASE `So70198786_02`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (13ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE DATABASE `So70198786_02`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (19ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER DATABASE CHARACTER SET utf8mb4;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (86ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE `IceCreams` (
`IceCreamId` int NOT NULL AUTO_INCREMENT,
`Name` longtext CHARACTER SET utf8mb4 NULL,
`Available` tinyint(1) NOT NULL,
CONSTRAINT `PK_IceCreams` PRIMARY KEY (`IceCreamId`)
) CHARACTER SET=utf8mb4;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`IceCreamId`, `Available`, `Name`)
VALUES (1, 1, 'Vanilla');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`IceCreamId`, `Available`, `Name`)
VALUES (2, 0, 'Chocolate');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `i`.`IceCreamId`, `i`.`Available`, `i`.`Name`
FROM `IceCreams` AS `i`
WHERE `i`.`Available` <> 0
Unrelated to the topic of this question, [MaxLength(255)] or .HasMaxLength(255) have no effect on non-string columns.
Related
I want to store list of string into mysql table as json. I saw there is support for this in pomelo entityframework. I followed this https://libraries.io/github/tuanbs/Pomelo.EntityFrameworkCore.MySql
Here is my entity
public class Project
{
public int Id {get;set;}
public string Title {get;set;}
public JsonObject<List<string>> Tags {get;set;}
}
But when _context.Database.EnsureDeleted(); is called it gives below error
Navigation property 'Tags' on entity type 'Project' is not virtual.
UseLazyLoadingProxies requires all entity types to be public,
unsealed, have virtual navigation properties, and have a public or
protected constructor.
But it is not navigation property that I have to add virtual keyword with it but is a column. Don't know what am I missing here.
Take a look at the following sample code, that is taken from my post on our GitHub repository, and works without issues:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Storage;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
public JsonObject<Energy> Energy { get; set; }
public JsonObject<List<string>> Comments { get; set; }
}
public class Energy
{
public double Kilojoules { get; set; }
public double Kilocalories { get; set; }
}
public class Context : DbContext
{
public virtual DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySql("server=127.0.0.1;port=3306;user=root;password=;database=So62301095",
b => b.ServerVersion(new ServerVersion("8.0.20-mysql")))
.UseLoggerFactory(LoggerFactory.Create(b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
internal class Program
{
private static void Main()
{
using (var context = new Context())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.IceCreams.AddRange(
new IceCream
{
Name = "Vanilla",
Energy = new Energy
{
Kilojoules = 866.0,
Kilocalories = 207.0
},
Comments = new List<string>
{
"First!",
"Delicious!"
}
},
new IceCream
{
Name = "Chocolate",
Energy = new Energy
{
Kilojoules = 904.0,
Kilocalories = 216.0
},
Comments = new List<string>
{
"My husband likes this one a lot."
}
});
context.SaveChanges();
}
using (var context = new Context())
{
var result = context.IceCreams
.OrderBy(e => e.IceCreamId)
.ToList();
Debug.Assert(result.Count == 2);
Debug.Assert(result[0].Name == "Vanilla");
Debug.Assert(result[0].Energy.Object.Kilojoules == 866.0);
Debug.Assert(result[0].Comments.Object.Count == 2);
Debug.Assert(result[0].Comments.Object[0] == "First!");
}
}
}
}
It generates the following SQL:
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.1.3 initialized 'Context' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: ServerVersion 8.0.20 MySql SensitiveDataLoggingEnabled DetailedErrorsEnabled
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (81ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
DROP DATABASE `So62301095`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (12ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE DATABASE `So62301095`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (66ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE `IceCreams` (
`IceCreamId` int NOT NULL AUTO_INCREMENT,
`Name` longtext CHARACTER SET utf8mb4 NULL,
`Energy` json NULL,
`Comments` json NULL,
CONSTRAINT `PK_IceCreams` PRIMARY KEY (`IceCreamId`)
);
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (15ms) [Parameters=[#p0='["First!","Delicious!"]', #p1='{"Kilojoules":866.0,"Kilocalories":207.0}', #p2='Vanilla' (Size = 4000)], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`Comments`, `Energy`, `Name`)
VALUES (#p0, #p1, #p2);
SELECT `IceCreamId`
FROM `IceCreams`
WHERE ROW_COUNT() = 1 AND `IceCreamId` = LAST_INSERT_ID();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[#p0='["My husband likes this one a lot."]', #p1='{"Kilojoules":904.0,"Kilocalories":216.0}', #p2='Chocolate' (Size = 4000)], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`Comments`, `Energy`, `Name`)
VALUES (#p0, #p1, #p2);
SELECT `IceCreamId`
FROM `IceCreams`
WHERE ROW_COUNT() = 1 AND `IceCreamId` = LAST_INSERT_ID();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `i`.`IceCreamId`, `i`.`Comments`, `i`.`Energy`, `i`.`Name`
FROM `IceCreams` AS `i`
ORDER BY `i`.`IceCreamId`
Take a close look at the IceCream.Comments property, that does exactly what you want.
On the same GitHub issue further below, you find another post by me, with a much more sophisticated example.
Also, we are going to implement full JSON support next for Pomelo (probably within a week).
It seems like EF Core is doing an INSERT instead of an UPDATE, and thus MySQL complains about a duplicate key. However, I am using the Update method on the DbSet and the entities do have primary keys set. This results in a DUPLICATE ENTRY error in MySql.
Running VS 2019, EF Core 3.1.1 and ASP.NET Core 3.1
Model (I do not use Fluent config for relationships, just Convention):
public class Vehicle
{
public long Id {get; set; } // PRIMARY KEY, AUTO INCREMENT
public string RegistrationNumber { get; set; }
public VehicleSession Session { get; set; }
}
public class VehicleSession
{
public long VehicleId { get; set; }
public Vehicle Vehicle { get; set; }
public string DeviceUuid { get; set; } // PRIMARY KEY
public string AuthenticationToken { get; set; }
public string OSName { get; set; }
public string OSVersion { get; set; }
public string DeviceModel { get; set; }
}
Database:
Table 'vehiclesessions' where the PRIMARY KEY is DeviceUuid and has key 'device2':
Controller:
A request comes in from somewhere. DeviceUuid is fetched from HTTP headers.
public async Task<ActionResult<VehicleLoginResponse>> Login(VehicleLogin login)
{
Request.Headers.TryGetValue(BaseConstants.DEVICE_UUID, out StringValues deviceUuid);
Vehicle vehicle = await vehicleService.Authenticate(login.Username, login.Password); // returns a Vehicle by asking dbContet: dbContext.Vehicles.FirstOrDefault(v => v.Username.Equals(username));
VehicleSession vs = await vehicleService.CreateSession(vehicle, login, deviceUuid);
// ...
}
Service:
This service creates a new VehicleSession object, assigns it to the Vehicle property, and does an .Update on the Vehicle, so that the new session is saved with it.
public async Task<VehicleSession> CreateSession(Vehicle vehicle, VehicleLogin vehicleLogin, string deviceUuid)
{
VehicleSession vs = new VehicleSession()
{
Vehicle = vehicle,
AuthenticationToken = someTokenFetchedFromSomewhere,
DeviceModel = vehicleLogin.DeviceModel,
DeviceUuid = deviceUuid, // is 'device2'
OSName = vehicleLogin.OSName,
OSVersion = vehicleLogin.OSVersion
};
vehicle.Session = vs;
dbContext.Vehicles.Update(vehicle);
await dbContext.SaveChangesAsync();
return vs;
}
It doesn't matter if I replace the new VehicleSession and assignment, with just editing an already existing Vehicle.Session, same error:
public async Task<VehicleSession> CreateSession(Vehicle vehicle, VehicleLogin vehicleLogin, string deviceUuid)
{
if (vehicle.Session == null)
vehicle.Session = new VehicleSession(); // never executes this line
vehicle.Session.AuthenticationToken = someTokenFetchedFromSomewhere;
vehicle.Session.DeviceModel = vehicleLogin.DeviceModel;
vehicle.Session.DeviceUuid = deviceUuid;
vehicle.Session.OSName = vehicleLogin.OSName;
vehicle.Session.OSVersion = vehicleLogin.OSVersion;
await dbContext.SaveChangesAsync();
return vehicle.Session;
}
When doing so, I get an error saying:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred
while updating the entries. See the inner exception for details. --->
MySql.Data.MySqlClient.MySqlException (0x80004005): Duplicate entry
'device2' for key 'vehiclesessions.PRIMARY' --->
MySql.Data.MySqlClient.MySqlException (0x80004005): Duplicate entry
'device2' for key 'vehiclesessions.PRIMARY'
The SQL produced:
Failed executing DbCommand (125ms) [Parameters=[#p0='?' (Size = 95), #p1='?' (Size = 95), #p2='?' (Size = 4000), #p3='?' (Size = 4000), #p4='?' (Size = 4000), #p5='?' (DbType = Int64)], CommandType='Text', CommandTimeout='30']
INSERT INTO `VehicleSessions` (`DeviceUuid`, `AuthenticationToken`, `DeviceModel`, `OSName`, `OSVersion`, `VehicleId`)
VALUES (#p0, #p1, #p2, #p3, #p4, #p5);
fail: Microsoft.EntityFrameworkCore.Update[10000]
An exception occurred in the database while saving changes for context type 'blabla'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Duplicate entry 'device2' for key 'vehiclesessions.PRIMARY'
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Duplicate entry 'device2' for key 'vehiclesessions.PRIMARY'
Why am I getting this error? It seems like an INSERT is done instead of an UPDATE, even though the Vehicle object is fetched from dbContext, passed along and has PRIMARY id set.
So, I think I might have found the issue.
As noted above, I first did a search to retrieve the Vehicle, like this:
Vehicle vehicle = dbContext.Vehicles.FirstOrDefault(v => v.Username.Equals(username));
In this case, the Vehicle.Session, if one existed, was not populated. Later on, I did the update as the code above shows, and it failed as noted above.
But, if I change the fetch code to this:
Vehicle entityVehicle = dbContext.Vehicles
.Include(x => x.Session)// <-- NEW!
.FirstOrDefault(v => v.Username.Equals(username));
then it works.
It doesn't matter if I assign the .Session a new VehicleSession(...) or if I change the properties in the existing object. It also doesn't matter if I use dbContext.Vehicles.Update(vehicle); before await dbContext.SaveChangesAsync();, the call to .Update(...) is not needed.
The only thing that made a difference, was to use .Include(...).
My own theory is:
When fetching the object from db, the EF Core "tracker" starts tracking. Property Session is NULL. When Session later is populated, the tracker detects that it was NULL but now is not NULL, thus, it figures that an INSERT is needed.
And then, if you populate the Session on fetch, tracker sees it is there, and should be updated.
I guess hibernate is trying to assign a String value fetched from the database to long.Have done many-to-one unidirectional mapping.I'm trying to display the values from the region table in a drop down in CorporateGroupForm.jsp
CorporateGroup.java
#Entity
#Table(name="corporate_group")
public class CorporateGroup extends BaseObject implements Serializable {
private Region region;
private Long id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="id")
public Region getRegion() {
return region;
}
public void setRegion(Region region) {
this.region = region;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
} }
corporateGroupForm.jsp
<li>
<appfuse:label styleClass="desc" key="corporateGroupDetail.region"/>
<select name="regionDesc">
<option value=""><fmt:message key="select.pleaseSelect"/></option>
<c:forEach var="region" items="${regionsList}">
<c:set var="selected" value="${corporateGroup.region ne null and corporateGroup.region.regionDesc eq region.regionDesc}"/>
<option ${selected ? 'selected' : ''} value="${region.regionDesc }">${region.regionDesc } </option>
</c:forEach>
</select>
</li>
DB:
CREATE TABLE `corporate_group` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`comment` text,`name` varchar(255) NOT NULL,`parent_id`bigint(20) DEFAULT NULL,`primary_contact_id` bigint(20) DEFAULT NULL,`account_manager_email` varchar(255) DEFAULT NULL,`dateCreated` datetime DEFAULT CURRENT_TIMESTAMP,`region_description` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `name` (`name`),KEY `FK61BCC225C8E0340A` (`parent_id`),KEY `FK61BC225F0655E4F` (`primary_contact_id`),KEY `FK_REGION_idx` (`region_description`),CONSTRAINT `fk_region` FOREIGN KEY (`region_description`) REFERENCES `region` (`region_description`) ON DELETE NO ACTION ON UPDATE NO ACTION,CONSTRAINT `FK61BC225F0655E4F` FOREIGN KEY (`primary_contact_id`) REFERENCES `app_user` (`id`),CONSTRAINT `FK61BCC225C8E0340A` FOREIGN KEY (`parent_id`) REFERENCES `corporate_group` (`id`)) ENGINE=InnoDB AUTO_INCREMENT=843 DEFAULT CHARSET=latin1;
CREATE TABLE `region` (`id` bigint(20) NOT NULL,`country_code` varchar(50) NOT NULL,country_name` varchar(100) NOT NULL,`time_zone` varchar(100) NOT NULL,`region_description` varchar(255) NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `description_UNIQUE` (`region_description`),KEY `id` (`id`),KEY `region_description` (`region_description`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Exception Stack Trace :
Hibernate: select corporateg0_.id as id2_,
corporateg0_.account_manager_email as account2_2_,
corporateg0_.comment as comment2_, corporateg0_.name as name2_,
corporateg0_.parent_id as parent6_2_, corporateg0_.primary_contact_id
as primary5_2_, corporateg0_.region_description as region7_2_ from
corporate_group corporateg0_ order by corporateg0_.name WARN
[http-bio-9080-exec-1] JDBCExceptionReporter.logExceptions(77) | SQL
Error: 0, SQLState: S1009 WARN [http-bio-9080-exec-1]
JDBCExceptionReporter.logExceptions(77) | SQL Error: 0, SQLState:
S1009 ERROR [http-bio-9080-exec-1]
JDBCExceptionReporter.logExceptions(78) | Invalid value for getLong()
- 'UK -UTC +0:00' ERROR [http-bio-9080-exec-1] JDBCExceptionReporter.logExceptions(78) | Invalid value for getLong()
- 'UK -UTC +0:00'
The error on the web page :
Data Access Failure
Hibernate operation: could not execute query; uncategorized SQLException for SQL [select corporateg0_.id as id2_, corporateg0_.account_manager_email as account2_2_, corporateg0_.comment as comment2_, corporateg0_.name as name2_, corporateg0_.parent_id as parent7_2_, corporateg0_.primary_contact_id as primary5_2_, corporateg0_.region_description as region6_2_ from corporate_group corporateg0_ order by corporateg0_.name]; SQL state [S1009]; error code [0]; Invalid value for getLong() - 'UK -UTC +0:00'; nested exception is java.sql.SQLException: Invalid value for getLong() - 'UK -UTC +0:00'
Region.java
#Entity
#Table(name = "region")
public class Region extends BaseObject implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private String countryCode;
private String countryName;
private String timeZone;
private String regionDesc;
#Column(name="country_code",nullable=false)
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
#Column(name="country_name",nullable=false)
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
#Column(name="time_zone",nullable=false)
public String getTimeZone() {
return timeZone;
}
public void setTimeZone(String timeZone) {
this.timeZone = timeZone;
}
#Column(name="region_description",nullable=false)
public String getRegionDesc() {
return regionDesc;
}
public void setRegionDesc(String regionDesc) {
this.regionDesc = regionDesc;
}
#Override
public String toString() {
StringBuffer strBuff = new StringBuffer();
if (getId() != null) {
strBuff = strBuff.append("ID:" + getId() + ",");
strBuff = strBuff.append("Country Name:" + getCountryName() + ",");
strBuff = strBuff.append("Country Code:" + getCountryCode() + ",");
strBuff = strBuff.append("Timezone:" + getTimeZone() + ",");
strBuff = strBuff.append("Region Description:" + getRegionDesc() + ",");
}
return strBuff.toString();
}
#Override
public boolean equals(Object o) {
// TODO Auto-generated method stub
if (!(o instanceof Region)) {
return false;
}
Region reg = (Region) o;
return !(regionDesc != null ? !regionDesc.equals(reg.getRegionDesc()) : reg.getRegionDesc() != null);
}
#Override
public int hashCode() {
// TODO Auto-generated method stub
int hashcode = 0;
if (this.regionDesc != null) {
hashcode = hashcode + this.regionDesc.hashCode();
}
return hashcode;
}
}
Now a different error :
ERROR [localhost-startStop-1 ContextLoader.initWebApplicationContext(215) | Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_filterChainProxyPostProcessor': BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.config.internalTransactionAdvisor': Cannot create inner bean '(inner bean)' of type [org.springframework.transaction.interceptor.TransactionInterceptor] while setting bean property 'transactionInterceptor'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#1': Cannot resolve reference to bean 'transactionManager' while setting bean property 'transactionManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in class path resource [applicationContext-dao.xml]: Cannot resolve reference to bean 'sessionFactory' while setting bean property 'sessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException:Error creating bean with name 'sessionFactory' defined in class path resource [applicationContext-dao.xml]: Invocation of init method failed; nested exception is org.hibernate.MappingException: Repeated column in mapping for entity: com.canvas8.model.CorporateGroup column: id (should be mapped with insert="false" update="false")
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:405)
at java.security.AccessController.doPrivileged(Native Method)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:220)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:881)
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:606)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:366)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4994)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5492)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1081)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1877)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
In your CorporateGroup entity class, you mapped region with region_description of Region entity which has a primary key of Long
#ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinColumn(name="region_description")
public Region getRegion() {
return region;
}
What you can do is you can map region member variable of CorporateGroup to primary key of Region entity class not the region_description.
Regarding this error :
Repeated column in mapping for entity:
com.canvas8.model.CorporateGroup column: id (should be mapped with
insert="false" update="false")
The error message is obvious, you have mapped same column twice. Refer this and fix the issue.
Hope this helps!
I was able to specify the referenced column name to resolve the issue for me. I didn't want to eager fetch the entities since it would only be used sometimes and didn't have permissions to modify the database schema.
Try this:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "region_description", referencedColumnName = "region_description")
private Region region;
I've created a database and added the employee table with the following code
CREATE TABLE IF NOT EXISTS `employee` (
`Idemployee` int(11) NOT NULL,
`fIrstname` varchar(45) DEFAULT NULL,
`lastname` varchar(45) DEFAULT NULL,
`emaIl` varchar(45) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
--
--
--
INSERT INTO `employee` (`Idemployee`, `fIrstname`, `lastname`, `emaIl`) VALUES
(2, 'Pranil', 'kharkar', 'someMail#gmail.com'),
(5, 'prasad', 'kharkar', 'someMail#gmail.com'),
(8, 'prasad', 'kharkar', 'someMail#gmail.com'),
(9, 'prasad', 'kharkar', 'someMail#gmail.com'),
(10, 'Pranil', 'kharkar', 'someMail#gmail.com'),
(11, 'Pranil', 'kharkar', 'someMail#gmail.com'),
(13, 'prasad', 'kharkar', 'someMail#gmail.com'),
(14, 'prasad', 'kharkar', 'someMail#gmail.com'),
(15, 'Murat', 'Kandemir', 'cmkTurkiye#gmail.com'),
(16, 'Murat2', 'Kandemir2', 'cmkTurkiye#gmail.com');
--
--
--
--
ALTER TABLE `employee`
ADD PRIMARY KEY (`Idemployee`);
--
--
--
--
--
--
ALTER TABLE `employee`
MODIFY `Idemployee` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=17;
and then created jpa project and created these 2 code pieces below for adding someone to table;
package com.thejavageek.jpa;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import com.thejavageek.jpa.entities.Employee;
public class Test {
public static void main(String[] args) throws IOException {
/* Create EntityManagerFactory */
EntityManagerFactory emf = Persistence
.createEntityManagerFactory("kurumsaljava");
/* Create and populate Entity */
Employee employee = new Employee();
employee.setFirstname("Ahmet");
employee.setLastname("Mercan");
employee.setEmail("cmkTurkiye#gmail.com");
//employee.setIdEmployee();
/* Create EntityManager */
EntityManager em = emf.createEntityManager();
/* Persist entity */
em.getTransaction().begin();
em.persist(employee);
em.getTransaction().commit();
/* CMK UPDATE
*/
/* Update routines begin*/
/*
int iEdit = 0;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter Employee Number To Edit");
//String iEdit = br.readLine();
try{
iEdit = Integer.parseInt(br.readLine());
}catch(NumberFormatException nfe){
System.err.println("Invalid Format!");
}
employee = em.find(Employee.class, iEdit);
em.getTransaction().begin();
employee.setFirstname("Murat7");
employee.setLastname("Celal");
System.out.println("Employee after updation :- " + employee);
em.getTransaction().commit();
*/
/*Update routines finish*/
// CMK DELETE
/* Delete routines begin*/
/*
int iRemove = 0;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter Employee Number To Remove");
try{
iRemove = Integer.parseInt(br.readLine());
}catch(NumberFormatException nfe){
System.err.println("Invalid Format!");
}
// Remove entity
employee = em.find(Employee.class, iRemove);
System.out.println(employee);
em.getTransaction().begin();
em.remove(employee);
em.getTransaction().commit();
*/
/*Delete routines finish*/
/* CMK
*/
/* Retrieve entity */
/*
employee = em.find(Employee.class, iEdit);
System.out.println(employee);
*/
/* Check whether entity is removed or not */
/*employee = em.find(Employee.class, 1);
System.out.println("Employee after removal :- " + employee);*/
}
}
and Employee.java;
package com.thejavageek.jpa.entities;
import java.io.Serializable;
import javax.persistence.*;
/**
* Entity implementation class for Entity: Employee
*
*/
#Entity
public class Employee implements Serializable {
/**
*
*/
private static final long serialVersionUID = -6984979000774926570L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int idEmployee;
private String email;
private String firstname;
private String lastname;
public Employee() {
}
public int getIdEmployee() {
return this.idEmployee;
}
public void setIdEmployee(int IdEmployee) {
this.idEmployee = IdEmployee;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getFirstname() {
return this.firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return this.lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
#Override
public String toString() {
return "Employee [email=" + email
+ ", firstname=" + firstname + ", lastname=" + lastname + "]";
}
}
and persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="kurumsaljava">
<class>com.thejavageek.jpa.entities.Employee</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/kurumsaljava?useSSL=false" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="" />
</properties>
</persistence-unit>
</persistence>
database name is kurumsaljava.I am getting these errors couldnt solve them
[EL Warning]: 2016-04-27 15:45:04.913--UnitOfWork(1576499395)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?L, F?RSTNAME, LASTNAME) VALUES ('cmkTurkiye#gmail.com', 'Ahmet', 'Mercan')' at line 1
Error Code: 1064
Call: INSERT INTO EMPLOYEE (EMAİL, FİRSTNAME, LASTNAME) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(Employee [email=cmkTurkiye#gmail.com, firstname=Ahmet, lastname=Mercan])
Exception in thread "main" javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?L, F?RSTNAME, LASTNAME) VALUES ('cmkTurkiye#gmail.com', 'Ahmet', 'Mercan')' at line 1
Error Code: 1064
Call: INSERT INTO EMPLOYEE (EMAİL, FİRSTNAME, LASTNAME) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(Employee [email=cmkTurkiye#gmail.com, firstname=Ahmet, lastname=Mercan])
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
at com.thejavageek.jpa.Test.main(Test.java:35)
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?L, F?RSTNAME, LASTNAME) VALUES ('cmkTurkiye#gmail.com', 'Ahmet', 'Mercan')' at line 1
Error Code: 1064
Call: INSERT INTO EMPLOYEE (EMAİL, FİRSTNAME, LASTNAME) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery(Employee [email=cmkTurkiye#gmail.com, firstname=Ahmet, lastname=Mercan])
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:900)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:962)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:631)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2002)
at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:298)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:377)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165)
at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:489)
at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1786)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1737)
at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:226)
at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:125)
at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4207)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1531)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitRootUnitOfWork(RepeatableWriteUnitOfWork.java:277)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitAndResume(UnitOfWorkImpl.java:1169)
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:132)
... 1 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?L, F?RSTNAME, LASTNAME) VALUES ('cmkTurkiye#gmail.com', 'Ahmet', 'Mercan')' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
at com.mysql.jdbc.Util.getInstance(Util.java:387)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:939)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3878)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3814)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2478)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2625)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2551)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861)
at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2073)
at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2009)
at com.mysql.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:5094)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1994)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:890)
... 32 more
waiting for your answers.
As RiggsFolly said above i changed the Employee class's this parts i's to I;
#Override
public String toString() {
return "Employee [email=" + email
+ ", firstname=" + firstname + ", lastname=" + lastname + "]";
}
}
and added some addition to persistence.xml's connection string;
localhost:3306/example?useUnicode=yes&characterEncoding=UTF-8&useSSL=false
and it is working thx
I have created a new .NET MVC 5 web application using Entity Framework 6 and a msyql database. I am using code/model first. The database server has a default storage engine of MyISAM, but I would like for the tables that EF creates to be InnoDb. Does anyone know if there is as way to specify the storage engine that EF will use in the CREATE TABLE statement?
Actually the engine used by MySQL EF provider is ALWAYS InnoDB and you can't change it without rewriting the DDL generator.
To try you can create a simple project and enable log on MySQL. You will notice that every create statement will terminate with engine=InnoDb auto_increment=0
For example this class
public class Blog
{
public int BlogId { get; set; }
[MaxLength(200)]
public string Name { get; set; }
[MaxLength(200)]
public string Topic { get; set; }
public DateTime LastUpdated { get; set; }
[DefaultValue(0)]
public int Order { get; set; }
public virtual List<Post> Posts { get; set; }
}
with standard MySQL EF provider migration, generates this MySQL DDL statement
CREATE TABLE `Blogs` (
`BlogId` INT NOT NULL auto_increment,
`Name` NVARCHAR(200),
`Topic` NVARCHAR(200),
`LastUpdated` DATETIME NOT NULL,
`Order` INT NOT NULL,
PRIMARY KEY (`BlogId`)
) engine = InnoDb auto_increment = 0
Where is engine = InnoDb from? It's hard coded in migration source code.
You can have a look at the migration source code
https://github.com/mysql/mysql-connector-net/blob/6.9/Source/MySql.Data.EntityFramework5/MySqlMigrationSqlGenerator.cs
method MySqlMigrationSqlGenerator.Generate(CreateTableOperation op).
The last statement is sb.Append(") engine=InnoDb auto_increment=0");
So, the right question should be how can I change the engine from InnoDB to another engine.
You can inherit MySqlMigrationSqlGenerator class and override the method, i.e.:
internal class MyOwnMigrationSqlGenerator : MySqlMigrationSqlGenerator
{
public MyOwnMigrationSqlGenerator()
{
Engine = "InnoDB";
}
public MyOwnMigrationSqlGenerator(string engine)
{
Engine = engine;
}
private readonly List<MigrationStatement> _specialStatements = new List<MigrationStatement>();
public string Engine { get; set; }
public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
{
List<MigrationStatement> migrationStatements = base.Generate(migrationOperations, providerManifestToken).ToList();
migrationStatements.AddRange(_specialStatements);
return migrationStatements;
}
protected override MigrationStatement Generate(CreateTableOperation op)
{
StringBuilder sb = new StringBuilder();
string tableName = TrimSchemaPrefix(op.Name);
var autoIncrementCols = (List<string>)(typeof(MySqlMigrationSqlGenerator).GetProperty("autoIncrementCols", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this));
var primaryKeyCols = (List<string>)(typeof(MySqlMigrationSqlGenerator).GetProperty("primaryKeyCols", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this));
sb.Append("create table " + "`" + tableName + "`" + " (");
if (op.PrimaryKey != null)
{
op.PrimaryKey.Columns.ToList().ForEach(col => primaryKeyCols.Add(col));
}
//columns
sb.Append(string.Join(",", op.Columns.Select(c => "`" + c.Name + "` " + Generate(c))));
// Determine columns that are GUID & identity
List<ColumnModel> guidCols = new List<ColumnModel>();
ColumnModel guidPk = null;
foreach (ColumnModel columnModel in op.Columns)
{
if (columnModel.Type == PrimitiveTypeKind.Guid && columnModel.IsIdentity && String.Compare(columnModel.StoreType, "CHAR(36) BINARY", true) == 0)
{
if (primaryKeyCols.Contains(columnModel.Name))
guidPk = columnModel;
guidCols.Add(columnModel);
}
}
if (guidCols.Count != 0)
{
var createTrigger = new StringBuilder();
createTrigger.AppendLine(string.Format("DROP TRIGGER IF EXISTS `{0}_IdentityTgr`;", TrimSchemaPrefix(tableName)));
createTrigger.AppendLine(string.Format("CREATE TRIGGER `{0}_IdentityTgr` BEFORE INSERT ON `{0}`", TrimSchemaPrefix(tableName)));
createTrigger.AppendLine("FOR EACH ROW BEGIN");
foreach (ColumnModel opCol in guidCols)
createTrigger.AppendLine(string.Format("SET NEW.{0} = UUID();", opCol.Name));
createTrigger.AppendLine(string.Format("DROP TEMPORARY TABLE IF EXISTS tmpIdentity_{0};", TrimSchemaPrefix(tableName)));
createTrigger.AppendLine(string.Format("CREATE TEMPORARY TABLE tmpIdentity_{0} (guid CHAR(36))ENGINE=MEMORY;", TrimSchemaPrefix(tableName)));
createTrigger.AppendLine(string.Format("INSERT INTO tmpIdentity_{0} VALUES(New.{1});", TrimSchemaPrefix(tableName), guidPk.Name));
createTrigger.AppendLine("END");
var sqlOp = new SqlOperation(createTrigger.ToString());
_specialStatements.Add(Generate(sqlOp));
}
if (op.PrimaryKey != null) // && !sb.ToString().Contains("primary key"))
{
sb.Append(",");
sb.Append("primary key ( " + string.Join(",", op.PrimaryKey.Columns.Select(c => "`" + c + "`")) + ") ");
}
string keyFields = ",";
autoIncrementCols.ForEach(col => keyFields += (!primaryKeyCols.Contains(col) ? string.Format(" KEY (`{0}`),", col) : ""));
sb.Append(keyFields.Substring(0, keyFields.LastIndexOf(",")));
sb.Append(string.Format(") engine={0} auto_increment=0", Engine));
return new MigrationStatement() { Sql = sb.ToString() };
}
private string TrimSchemaPrefix(string table)
{
if (table.StartsWith("dbo.") || table.Contains("dbo."))
return table.Replace("dbo.", "");
return table;
}
}
Then, in your migration configuration you can specify your own sql generator.
internal sealed class MyContextMigrationConfiguration : DbMigrationsConfiguration<MyContext>
{
public MyContextMigrationConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
SetSqlGenerator("MySql.Data.MySqlClient", new MyOwnMigrationSqlGenerator("MyPreferredEngine"));
}
}
EDIT
There was a bug on MyOwnMigrationSqlGenerator class. Probably the best thing is to rewrite all MySqlMigrationSqlGenerator. In this case I just fixed the class accessing private fields of MySqlMigrationSqlGenerator (that is quite bad).