I am trying to read data from my database. I have created a table called ForumSections, here is its table definition.
CREATE TABLE [dbo].[ForumSection] (
[Id] INT IDENTITY (0, 1) NOT NULL,
[SectionName] NVARCHAR (200) NOT NULL,
[TopicsinSection] INT NOT NULL,
[SectionDescription] NVARCHAR (MAX) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
When I try to call from the database I get a EntityCommandExecutionException with a InnerException message of Invalid object name 'dbo.ForumSections'.
Here is the code used to call the database.
First setting up the database context:
public class MainDbContext : DbContext
{
public MainDbContext()
: base("name=DefaultConnection")
{
}
public DbSet<ForumSection> ForumSection { get; set; }
}
Then calling it:
var db = new MainDbContext();
db.ForumSection.Find(0); //Error happens on this line
The row with index of 0 is appropriately filled out.
What are my options for resolving this?
By default, EntityFramework tries to look for the table pluralizing the name of the class. That's why it is adding the 's' to the ForumSection.
You can disable that option. This will help you solve your problem:
Why does EF 5.x use plural name for table?
Related
I am using the below code:
private static String file="create-table.yml";
public static void main(String[] args) throws Exception {
Database database =createOfflineDatabase("offline:oracle");
Liquibase liquibase = new Liquibase(file, new ClassLoaderResourceAccessor(), database);
liquibase.update("test");
liquibase.dropAll();
}
private static Database createOfflineDatabase(String url) throws Exception {
DatabaseConnection databaseConnection = new OfflineConnection(url, new ClassLoaderResourceAccessor());
return DatabaseFactory.getInstance().openDatabase(url, null, null, null, null);
}
Getting this exception :
Exception in thread "main" liquibase.exception.MigrationFailedException: Migration failed for change set create-table.yml::create-table.yml::vishwakarma:
Reason: liquibase.exception.DatabaseException: Cannot execute commands against an offline database
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:619)
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:51)
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:79)
at liquibase.Liquibase.update(Liquibase.java:214)
at liquibase.Liquibase.update(Liquibase.java:192)
at liquibase.Liquibase.update(Liquibase.java:188)
at liquibase.Liquibase.update(Liquibase.java:181)
at com.test.liquibase.LiquibaseTest.main(LiquibaseTest.java:27)
Am I doing something wrong or missing something? Please help.
Thanks in Advance
The Liquibase class you are using and that is in most example code doesn't seem to have an "updateSQL". You can programmatically invoke the main method passing in the necessary --url parameters to get it to do the work. for example:
liquibase.integration.commandline.Main.main(new String[]{"--changeLogFile=src/test/resources/db.changelog.xml"
,"--outputFile=target/updateSql.txt"
,"--url=offline:unknown?outputLiquibaseSql=true"
, "updateSQL"});
Will generate:
-- *********************************************************************
-- Update Database Script
-- *********************************************************************
-- Change Log: src/test/resources/db.changelog.xml
-- Ran at: 12/04/20 11:51
-- Against: null#offline:unknown?outputLiquibaseSql=true
-- Liquibase version: 3.8.9
-- *********************************************************************
CREATE TABLE DATABASECHANGELOG (ID VARCHAR(255) NOT NULL, AUTHOR VARCHAR(255) NOT NULL, FILENAME VARCHAR(255) NOT NULL, DATEEXECUTED datetime NOT NULL, ORDEREXECUTED INT NOT NULL, EXECTYPE VARCHAR(10) NOT NULL, MD5SUM VARCHAR(35), DESCRIPTION VARCHAR(255), COMMENTS VARCHAR(255), TAG VARCHAR(255), LIQUIBASE VARCHAR(20), CONTEXTS VARCHAR(255), LABELS VARCHAR(255), DEPLOYMENT_ID VARCHAR(10));
-- Changeset src/test/resources/db.changelog.xml::createTable-example::liquibase-docs
CREATE TABLE public.person (address VARCHAR(255));
INSERT INTO DATABASECHANGELOG (ID, AUTHOR, FILENAME, DATEEXECUTED, ORDEREXECUTED, MD5SUM, DESCRIPTION, COMMENTS, EXECTYPE, CONTEXTS, LABELS, LIQUIBASE, DEPLOYMENT_ID) VALUES ('createTable-example', 'liquibase-docs', 'src/test/resources/db.changelog.xml', CURRENT_TIMESTAMP, 1, '8:49e8eb557129b33d282c4ad2fdc5d4d9', 'createTable tableName=person', '', 'EXECUTED', NULL, NULL, '3.8.9', '6688703163');
And also outputs a databasechangelog.csv that tracks what should be the state of the DATABASECHANGELOG for subsequent runs.
If you edit the code to load "db.changelog2.xml" with something new in it with a different ID and tell it to write out the sql to a new file that will only have the second change set in it. This is because the databasechangelog.csv will be used to know what was previously run. The CSV will then be updated for the next time:
"ID","AUTHOR","FILENAME","DATEEXECUTED","ORDEREXECUTED","EXECTYPE","MD5SUM","DESCRIPTION","COMMENTS","TAG","LIQUIBASE","CONTEXTS","LABELS","DEPLOYMENT_ID"
"createTable-example","liquibase-docs","src/test/resources/db.changelog.xml","2020-04-12T12:10:16.989","2","EXECUTED","8:49e8eb557129b33d282c4ad2fdc5d4d9","createTable tableName=person","","","3.8.9","()","","6689816939"
"createTable-example2","liquibase-docs","src/test/resources/db.changelog2.xml","2020-04-12T12:26:56.664","4","EXECUTED","8:3740614394b969eeb1edcc9fd0187bdb","createTable tableName=person2",,"","3.8.9","()","","6690816645"
Warning: It seems that if you call the main method directly it will internally call System.exit() so if you really wanted to run updateSQL inside a tool you will need to look at the code inside that main class to figure out how to do it without the JVM being terminated by System.exit().
Offline databases are something that Liquibase came up with to allow you to generate SQL for updating a database that the liquibase user is not able to update directly - they are not able to be updated. See this documentation for more details about offline databases.
I'm having trouble with concurrency checks using EF6 and MySQL.
The problem I'm having is that I get a concurrency exception thrown when I try to save data to the database. If you examine the sql that is output to the console it tries to query the concurrency field from the database using the old value in the where clause. Because this field has been updated by the database.
Environment:
Windows 7 64 bit
Visual Studio 2013
Nuget packages installed:
EF 6.0.1
MySql.ConnectorNET.Data 6.8.3.2
MySql.ConnectorNET.Entity 6.8.3.2
Demo Database SQL:
DROP DATABASE IF EXISTS `bugreport`;
CREATE DATABASE IF NOT EXISTS `bugreport`;
USE `bugreport`;
DROP TABLE IF EXISTS `test`;
CREATE TABLE IF NOT EXISTS `test` (
`TestId` int(10) NOT NULL AUTO_INCREMENT,
`AStringField` varchar(50) DEFAULT NULL,
`DateModified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`TestId`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
INSERT INTO `test` (`TestId`, `AStringField`, `DateModified`) VALUES
(1, 'Initial Value', '2014-07-11 09:15:52');
Demo code:
using System;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace BugReport
{
class Program
{
static void Main(string[] args)
{
using (var context = new BugReportModel())
{
context.Database.Log = (s => Console.WriteLine(s));
var firstTest = context.tests.First();
firstTest.AStringField = "First Value";
// Exception is thrown when changes are saved.
context.SaveChanges();
Console.ReadLine();
}
}
}
public class BugReportModel : DbContext
{
public BugReportModel()
: base("name=Model1")
{
}
public virtual DbSet<test> tests { get; set; }
}
[Table("test")]
public class test
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TestId { get; set; }
[StringLength(50)]
public string AStringField { get; set; }
[ConcurrencyCheck()]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Column(TypeName = "timestamp")]
public System.DateTime DateModified { get; set; }
}
}
Update:
Filed bug with MySql.
You should be trying to use the DB Timestamp / Rowversion feature.
In EF you declare a ByteArray and nominate it as the Concurrency check field.
DB sets the value on creation. All subsequent updates can check the value hasnt changed
DB updates rowversion as appropriate. This approach works on SQL server.
It should behave the same way on MYSql.
public abstract class BaseObject {
[Key]
[Required]
public virtual int Id { set; get; }
[ConcurrencyCheck()]
public virtual byte[] RowVersion { get; set; }
}
or via fluent if you like
// Primary Key
this.HasKey(t => t.Id);
// Properties
//Id is an int allocated by DB , with string keys, no db generation now
this.Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); // default to db generated
this.Property(t => t.RowVersion)
.IsRequired()
.IsFixedLength()
.HasMaxLength(8)
.IsRowVersion(); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Docu on the optimistic concurrency pattern
Workarround with Interceptor
I see that MySQL Connector bug pointed in question do not have a fix yet (since 2014) and I wrote that "solution" (I know this is ugly) until the they fix it.
I create an DBCommandInterceptor and override the ReaderExecuting to replace the equal operator (=) in last WHERE to a not equal operator (<>) because the pattern for the update is something like "UPDATE ...; SELECT ... WHERE (row_version_field = #parameter)"
In the code bellow replace row_version in regular expression with the name of your row version field.
public class ConcurrencyFixInterceptor : DbCommandInterceptor
{
private static Regex concurrencyPattern =
new Regex(#"^UPDATE[\S\s]+SELECT[\S\s]+\(.?row_version.?\s(=)\s#[\w\d]+\)$",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
Match macth = concurrencyPattern.Match(command.CommandText);
if (macth.Success)
{
command.CommandText =
command.CommandText.
Remove(macth.Groups[1].Index, 1).
Insert(macth.Groups[1].Index, "<>");
}
base.ReaderExecuting(command, interceptionContext);
}
}
I use a row version in MySQL with a TIMESTAMP(5) field type.
I have just submitted a PR to MySQL .NET Connector v6.9.10 that provides a workaround solution for this issue.
The workaround avoids use of TIMESTAMP or DATETIME values to perform optimistic locking using a safer BIGINT RowVersion value that is incremented via a BEFORE UPDATE trigger. This fix will now support optimistic locking with an external (non-EF) application. If I can fix a 2nd bug related to TIMESTAMP / DATETIME then ConcurrencyCheck should work with these types as well.
EF6:
public class MyTable
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public virtual int Id { get; set; }
[Required, MaxLength(45)]
public virtual string Name { get; set; }
[ConcurrencyCheck, DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Column(TypeName = "bigint")]
public virtual long RowVersion { get; set; }
}
SQL:
CREATE TABLE IF NOT EXISTS `mytable` (
Id int(11) NOT NULL,
Name varchar(45) NOT NULL,
RowVersion bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB
CREATE TRIGGER `trg_mytable_before_update`
BEFORE UPDATE ON `mytable`
FOR EACH ROW SET NEW.RowVersion = OLD.RowVersion + 1;
TIMESTAMP Solution?
I'm also investigating how to performing optimistic locking with a TIMESTAMP field.
Firstly, you need to use a more fine grained timestamp value.
So for example if you use the following, your timestamp value will be truncated to the nearest second (not very safe for optimistic locking).
UpdatedAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIME ON UPDATE CURRENT_TIME
Instead you should use following to record microsecond precision.
UpdatedAt TIMESTAMP(6) NOT NULL DEFAULT NOW(6) ON UPDATE NOW(6)
Secondly, I'm observing a bug that I'm reproducing within the environment of the MySQL .NET Connector unit test suite combined with the PR patch I've just submitted. EF6 now generates the correct optimistic locking SQL to perform an UPDATE followed by the SELECT (now fixed) that returns the updated TIMESTAMP field. However the MySQL connector returns a zero TIMESTAMP (0000-00-00 00:00:00.000000) even though executing the exact same UPDATE and SELECT in MySQL Workbench it returns a valid non-zero TIMESTAMP value. I've observed the packets read via the connection socket return the string '0000-00-00 00:00:00.000000' so its probably related to the MySQL session configuration in some way. Hints welcome! I'm currently testing this with MySQL v5.6.26 (Windows).
Multiple optimistic lock fields
In our case, we have a legacy MS-Access app that uses a TIMESTAMP in most tables to perform optimistic locking. This is a convenient solution for MS-Access as it detects the presence of any TIMESTAMP column and automatically applies optimistic locking to this column when it finds one.
Since we currently don't have optimistic locking working with EF6 for TIMESTAMP columns we've added a second optimistic lock column on each table we care about by creating a BIGINT RowVersion column as that is incremented via a BEFORE INSERT trigger. So now for each UPDATE both the existing TIMESTAMP column and the new RowVersion column are update so either can be used to detect a change. Not ideal but it works!
I'm using Fanbatis framework to access a MySQL database. The documentation here: http://www.talesframework.org/fanbatis/ says that I can use the #Column annotation to map a class attribute to a column with a different name:
#Column{name="xx"} - By default column name is assumed to be the field name,
to change this use this annotation on a field
I have this class...
using fanbatis
#Table {name = "APPS"}
class App
{
#Primary
Str? id
Str? name
#Column{name="description"}
Str? descr
}
And the APPS table created with this SQL:
create table APPS (
ID varchar(36) not null,
NAME varchar(100) not null,
DESCRIPTION varchar(400) ,
primary key (ID)
);
And I'm trying to retrieve a record from the database using this DAO
class AppDao
{
static App? findById(Str id)
{
S<|
select * from apps where id = #{id}
|>
}
}
My code compiles fine, but when I run it, passing in the ID of an existing record, it retrieves the record from the database and set the values of the attributes that match the column names, but the app.descr remains null. However, if I just remove the #Column annotation from the "descr" attribute and rename it to match the column ("description"), then the code runs fine and returns the expected values.
-- Run: auth::TestAppDao.testFindById...
Test setup!
app.id: 0615a6cb-7bda-cc40-a274-00130281bd51
app.name: MyApp
app.descr: null
TEST FAILED
sys::TestErr: Test failed: null != "MyApp descr" [sys::Str]
fan.sys.Test.err (Test.java:206)
fan.sys.Test.fail (Test.java:198)
fan.sys.Test.verifyEq (Test.java:121)
fan.sys.Test.verifyEq (Test.java:90)
auth::TestAppDao.testFindById (TestAppDao.fan:36)
java.lang.reflect.Method.invoke (Unknown)
fan.sys.Method.invoke (Method.java:559)
fan.sys.Method$MethodFunc.callList (Method.java:204)
fan.sys.Method.callList (Method.java:138)
fanx.tools.Fant.runTest (Fant.java:191)
fanx.tools.Fant.test (Fant.java:110)
fanx.tools.Fant.test (Fant.java:32)
fanx.tools.Fant.run (Fant.java:284)
fanx.tools.Fant.main (Fant.java:327)
Test teardown!
Am I doing something wrong or is this a bug in Fanbatis?
It could be due to case sensitivity - see MySQL 9.2.2. Identifier Case Sensitivity
Although database and table names are not case sensitive on some
platforms, you should not refer to a given database or table using
different cases within the same statement. The following statement
would not work because it refers to a table both as my_table and as
MY_TABLE:
mysql> SELECT * FROM my_table WHERE MY_TABLE.col=1;
Try:
#Column{name="DESCRIPTION"}
It's also mentioned in this question.
I am using Entity Framework with MySQL and it's giving me a NullReferenceException every time I try to insert data.
I can insert data directly by creating a command but when I use Entity Framework it bombs out.
Entity Framework will select from tables or update tables so perhaps this is something to do with the primary key
The following exception is thrown from the SaveChanges() method.
failed: System.NullReferenceException : Object reference not set to an instance of an object.
at MySql.Data.Entity.ListFragment.WriteSql(StringBuilder sql)
at MySql.Data.Entity.SelectStatement.WriteSql(StringBuilder sql)
at MySql.Data.Entity.InsertStatement.WriteSql(StringBuilder sql)
at MySql.Data.Entity.SqlFragment.ToString()
at MySql.Data.Entity.InsertGenerator.GenerateSQL(DbCommandTree tree)
at MySql.Data.MySqlClient.MySqlProviderServices.CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree)
at System.Data.Common.DbProviderServices.CreateCommandDefinition(DbCommandTree commandTree)
at System.Data.Common.DbProviderServices.CreateCommand(DbCommandTree commandTree)
at System.Data.Mapping.Update.Internal.UpdateTranslator.CreateCommand(DbModificationCommandTree commandTree)
at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.CreateCommand(UpdateTranslator translator, Dictionary`2 identifierValues)
at System.Data.Mapping.Update.Internal.DynamicUpdateCommand.Execute(UpdateTranslator translator, EntityConnection connection, Dictionary`2 identifierValues, List`1 generatedValues)
at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter)
at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Objects.ObjectContext.SaveChanges()
EF 5
public decimal CreateAlertNotification2(ulong alertServiceId, Alerting.alert_service_notification_type notificationType, string recipientName, string recipientEndpoint)
{
using (risk_fleetEntities dbContext = new risk_fleetEntities())
{
var newNotification = new alert_service_notification();
newNotification.sys_alert_service_id = alertServiceId;
newNotification.name = recipientName;
newNotification.notification_type = Enum.GetName(typeof(Alerting.alert_service_notification_type), notificationType);
newNotification.recipient = recipientEndpoint;
dbContext.alert_service_notification.AddObject(newNotification);
dbContext.SaveChanges();
return newNotification.id;
}
}
MySQL 5
CREATE TABLE `alert_service_notification` (
`id` bigint(20) unsigned AUTO_INCREMENT PRIMARY KEY,
`sys_alert_service_id` bigint(20) unsigned NOT NULL,
`notification_type` ENUM("SMS", "Email"),
`name` CHAR(50),
`recipient` CHAR(50),
FOREIGN KEY (`sys_alert_service_id`) REFERENCES `alert_service`(`id`)
);
The EF doesn't support unsigned ints, so in this case it has to store the bigint as decimal as Int64's not big enough (9,223,372,036,854,775,807 max value vs unsigned bigint's 18,446,744,073,709,551,615). It will also store an unsigned int as Int64 instead of Int32 for the same reason.
If you try and change the type in the EF designer it doesn't quite do everything correctly and throws errors like this at runtime. You can actually open the edmx and edit it to fix this, you trick the EF into assuming the column isn't really unsigned. Simply find all references of your column and make sure none of them mention Int64/decimal (depending if you want an Int32/Int64). However if you are doing this, your database could possibly return invalid values if you ever went over the max value.
So the easier and correct fix is not to use unsigned or change the existing columns from unsigned. Certain MySQL designer tools default to unsigned id columns, so watch out for that!
My solution was very simple. Just edited the database to have no "unsigned" variables, and built the model from that. Works like a charm, and no manual editing needed. Also, changing them back to unsigned after generating the model seems to have no issues. :)
I am using Struts2, Spring and Hibernate in my application and database is MySQL 5.5. I have this table in database:
create table if not exists campaigns(
id int(10) not null auto_increment,
campaignId int(25) not null unique,
createdBy int(25) not null REFERENCES users(userId),
campaignName varchar(255) not null,
subject varchar(500),
body varchar(50000),
modifiedOn TIMESTAMP,
triggeredOn date,
numberOfTargets int(10),
primary key (id, campaignId)
);
And I save and update the "Campaign" objects with the following methods (hibernate-mapping through hbm files) :
public boolean addCampaign(long createdBy, String campaignName) throws NoSuchAlgorithmException {
Campaign campaignObject = new Campaign();
SecureRandom generatedHash = SecureRandom.getInstance("SHA1PRNG");
campaignObject.setCampaignId(new Integer(generatedHash.nextInt()));
campaignObject.setCreatedBy(createdBy);
campaignObject.setCampaignName(campaignName);
getHibernateTemplate().save(campaignObject);
getSession().flush();
return true;
}
public Date updateCampaign(String campaignId, String subject, String body) throws NumberFormatException {
Campaign campaign = getCampaignByCampaignId(Long.parseLong(campaignId));
if(campaign != null) {
campaign.setSubject(subject);
campaign.setBody(body);
getHibernateTemplate().save(campaign);
getSession().flush();
return campaign.getModifiedOn();
}
return null;
}
The "modifiedOn" column updates when I run a update query on database. But hibernate is failing to update it. Thanks for your time.
First of all, save is for inserting a new entity. You should not use it when updating an attached entity. An attached entity's state is automatically written in the database (if changed) at flush time. You don't need to call anything to have the state updated.
And Hibernate won't magically re-read the row it has inserted/updated to get the generated timestamp. A specific #Generated annotation is needed to do that. But it will decrease the performance of the application.
I would use a pre-insert/pre-update hook to set the modifiedOn value programmatically in the entity, and avoid auto-modified timestamps in the database.
In addition to JB Nizet's response, if modifiedOn is being updated by a trigger, take a look at org.hibernate.Session#refresh().
In case the field is updated by a trigger, when hibernate saves the entity it has the old date, the trigger udpates the record at DB level (not the hibernate entity), and then when the Session closes, at commit time, Hibernate sees the entity as dirty because the modifiedOn field has a different value in DB. So, another update is launched and it is as if the trigger never updated the field. refresh() will update the entity's state with the one from the DB after the update, and the trigger execution, so they can be in synch at commit time.
public Date updateCampaign(String campaignId, String subject, String body)
throws NumberFormatException {
Campaign campaign = getCampaignByCampaignId(Long.parseLong(campaignId));
if(campaign != null) {
campaign.setSubject(subject);
campaign.setBody(body);
getHibernateTemplate().update(campaign);
getSession().flush();
getSession.refresh(campaign);
return campaign.getModifiedOn();
}
return null;
}