I'm working on a legacy application with MySQL DB.
Here's the relevant table:
RESERVATION
============
id: int
creation_date: tinyblob
...other stuff...
When the entity is created, the creation_date is calculated using Java's LocalDate.now().
When I select the data from the table (in MySQL Workbench), I see only "BLOB" in the creation_date column. When I click "Open value in editor", I get something like:
’ sr
java.time.Ser]º"H² xpw àx
When I double click the field, nothing happens, it won't let me enter the editing mode.
Is there any way to directly edit the date stored like this? I could create a Java function that will insert some dates other than LocalDate.now(), but it would be easier to do this manually in the Workbench (or some other application). Since this is a legacy software, I'm not able to alter the column type.
Tnx.
Here is the solution. This guy helped me: https://stackoverflow.com/a/8504540/1177067
Create a new class which extends AttributeConverter:
import java.time.LocalDateTime;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Object> {
#Override
public Object convertToDatabaseColumn(LocalDateTime locDateTime) {
return locDateTime == null ? null : Timestamp.valueOf(locDateTime);
}
#Override
public LocalDateTime convertToEntityAttribute(Object sqlTimestamp) {
if (sqlTimestamp != null) {
try {
byte[] dbBytes = (byte[]) sqlTimestamp;
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(dbBytes));
LocalDateTime covertedDateFromBytes = (LocalDateTime) ois.readObject();
ois.close();
return covertedDateFromBytes;
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
return null;
}
}
Be aware that:
#Override
public Object convertToDatabaseColumn(LocalDateTime locDateTime) {
return locDateTime == null ? null : Timestamp.valueOf(locDateTime);
}
is storing the date in a new way (as a Timestamp) so decide if you might want to go with the tinyblob further or use a new approach with Timestamp.
Related
I have NHibernate XML mapping files that work wonderfully in MSSQL Databases. An example of a table is:
<class name="Worm" table="`Worms`" schema="`dbo`">
Now I need to use the exact same mapping file (unchanged) to generate a MariaDB (or MySQL) database. Clearly, such databases do not have schemas. So, what I'm trying to do is to create a naming convention so that the 'schema' becomes the prefix of a table, e.g. 'dbo_Worm'.
I've tried using the
var schemaUpdate = new NHibernate.Tool.hbm2ddl.SchemaUpdate(configuration);
by adding a custom Naming Strategy class into the 'configuration'. For now my custom class does nothing: just throws NotImplementedExceptions():
public class MyCustomNamingStrategy : INamingStrategy
{
public static MyCustomNamingStrategy Instance => new MyCustomNamingStrategy();
public string ClassToTableName(string className)
{
throw new NotImplementedException();
}
public string PropertyToColumnName(string propertyName)
{
throw new NotImplementedException();
}
public string TableName(string tableName)
{
throw new NotImplementedException();
}
public string ColumnName(string columnName)
{
throw new NotImplementedException();
}
public string PropertyToTableName(string className, string propertyName)
{
throw new NotImplementedException();
}
public string LogicalColumnName(string columnName, string propertyName)
{
throw new NotImplementedException();
}
}
The reasons are two:
I've never reached the breakpoints of my MyCustomNamingStrategy
class to begin with, so I don't even know if this is the way to go.
Will it give me any information with regards to the 'schema'? I
don't know...
The code that calls the SchemaUpdate of the tool completely ignores the custom naming strategy and a MySQL Exception is thrown
stating that no 'dbo' database is found (duh....)
Having tried everything and searched everywhere I'm turning to you for assistance.
Can anyone help me
Keep the exact same XML Mapping File, yet
Produce tables prefixed with their schema names ?
Any hints would be greatly appreciated!
Finally found a solution:
public override void RemoveSchemas(NHibernate.Cfg.Configuration configuration)
{
foreach (var clsMapping in configuration.ClassMappings)
{
clsMapping.Table.Schema = null;
if ((clsMapping as NHibernate.Mapping.RootClass) != null) (clsMapping as NHibernate.Mapping.RootClass).CacheRegionName = null;
if (clsMapping.IdentityTable != null)
{
clsMapping.IdentityTable.Schema = null;
var identifier = clsMapping.IdentityTable.IdentifierValue as NHibernate.Mapping.SimpleValue;
if (identifier != null)
{
if(identifier?.IdentifierGeneratorProperties?.ContainsKey("schema") == true)
{
identifier.IdentifierGeneratorProperties["schema"] = null;
}
}
}
}
foreach (var colMapping in configuration.CollectionMappings)
{
colMapping.Table.Schema = null;
if (colMapping.CollectionTable != null) colMapping.CollectionTable.Schema = null;
colMapping.CacheRegionName = null;
}
}
I've another issue (seems to be the same thing as : stored procedure 'auto_pk_for_table' not found) But I put auto-increment and unique index for the ID and 'Database-Generated' in the primary key with my auto-increment field, See :
public abstract class _DateInfo extends CayenneDataObject {
public static final String ENDDATETIME_PROPERTY = "enddatetime";
public static final String STARTDATETIME_PROPERTY = "startdatetime";
public static final String USER_ID_PROPERTY = "userId";
public static final String DATEINFOID_PK_COLUMN = "DATEINFOID";
public static final String USERID_PK_COLUMN = "USERID";
public void setEnddatetime(Date enddatetime) {
writeProperty(ENDDATETIME_PROPERTY, enddatetime);
}
public Date getEnddatetime() {
return (Date)readProperty(ENDDATETIME_PROPERTY);
}
public void setStartdatetime(Date startdatetime) {
writeProperty(STARTDATETIME_PROPERTY, startdatetime);
}
public Date getStartdatetime() {
return (Date)readProperty(STARTDATETIME_PROPERTY);
}
public void setUserId(int userId) {
writeProperty(USER_ID_PROPERTY, userId);
}
public int getUserId() {
Object value = readProperty(USER_ID_PROPERTY);
return (value != null) ? (Integer) value : 0;
}
}
I tried to save a local time when I click on the save button :
Button save = new Button("Save", event -> {
DateInfoFactory date = CayenneUtil.getContext().newObject(
DateInfoFactory.class);
date.setUserId(userIdSelected);
if (startTime.getValue() != null) {
LocalTime startDate = startTime.getValue();
date.setStartdatetime(toDate(startDate));
}
date.getObjectContext().commitChanges();
});
save.addStyleName(ValoTheme.BUTTON_PRIMARY);
But then, I received this error :
juin 12, 2017 9:38:32 PM org.apache.cayenne.log.CommonsJdbcEventLogger logQuery
INFOS: LOCK TABLES AUTO_PK_SUPPORT WRITE
juin 12, 2017 9:38:32 PM org.apache.cayenne.log.CommonsJdbcEventLogger logQuery
INFOS: UNLOCK TABLES
juin 12, 2017 9:38:32 PM com.vaadin.server.DefaultErrorHandler doDefault
GRAVE:
org.apache.cayenne.CayenneRuntimeException: [v.4.0.M5 Feb 24 2017 07:47:55] Commit Exception
..
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'mam.auto_pk_support' doesn't exist
My table in MySQL is :
What should I do?
Thanks,
There can be several options how you can fix your code.
You have compound PK (dateinfoid + userid) is this intended?
Probably you should use single column PK (i.e. only dateinfoid) as it's uniquely identifies objects in your case and potentially will save you from other troubles.
If compound PK is intentional then make sure you provide non zero value in userIdSelected or otherwise Cayenne will try to provide it via auto_pk_support table.
I have a Date field in my DB and I'm trying to update it to the current Date when I press the submit button on my webpage but it does not update. I believe I'm doing the correct steps but here is my code.
Controller:
public ActionResult TakeInventory(int? AssetNum, string owners, string locationId, string clientId)
{
ViewBag.LocationId = new SelectList(db.Locations, "LocationKey", "LocationName");
ViewBag.ClientId = new SelectList(db.ClientSites, "ClientSiteKey", "ClientSiteName");
var records = from s in db.Assets select s;
if (AssetNum != 0)
{
records = records.Where(c => c.AssetKey == AssetNum);
}
if (!String.IsNullOrEmpty(owners))
{
records = records.Where(x => x.InventoryOwner.Equals(owners));
}
if (!String.IsNullOrEmpty(locationId))
{
int locnum = Convert.ToInt32(locationId);
records = records.Where(x => x.LocationKey == locnum);
}
if (!String.IsNullOrEmpty(clientId))
{
int clinum = Convert.ToInt32(clientId);
records = records.Where(x => x.ClientSiteKey == clinum);
}
else
{
return View(records);
}
return View(records);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult TakeInventory([Bind(Include = "InventoryDate")] Asset asset)
{
if (ModelState.IsValid)
{
db.Entry(asset).State = EntityState.Modified;
asset.InventoryDate = DateTime.Now;
db.Assets.Add(asset);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(asset);
}
View:
#foreach (var items in Model)
{
<p>Last Inventory Date: #Html.DisplayFor(modelItem => items.InventoryDate) </p>
<input type="submit" value="Submit" />
Model:
public partial class Asset
{
public System.DateTime InventoryDate { get; set; }
public Asset()
{
InventoryDate = DateTime.Now;
}
}
You want to retrieve the Asset entity again before updating again.
For example,
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult TakeInventory([Bind(Include = "InventoryDate")] Asset asset)
{
if (ModelState.IsValid)
{
var entity = (from s in db.Assets where AssetNum == asset.AssetNum Select s).FirstOrDefalt();
entity.InventoryDate = DateTime.Now;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(asset);
}
Is a bad practice:
asset.InventoryDate = DateTime.Now;
At least, you need:
1. SaveChanges() your DbContext
2. Your DateTime field in backend must be Nullable or NotNull in Db (there is no inmpicit conversion)
But the real trouble is timezones. Its all works fine, if you have only one instance in only one datacenter and all your clients is from only one small and beauty country (one timezone wide)
DateTime.Now returns you local mashine timezone time.
If you use your 'entity.InventoryDate' in any kind of requests query it can return confused rezults, and can be surprized with funny result: for ex., value with tomorrow datetime relatively to you :)
For Web-services always cast to UTC that kind of fields, or use triggers or default expression for this kind of fields inside your DB engine
P.S. Russia is 11 timezones wide, i know what i'm talking about
Why you are passing the Current date , there is no need for that you can you Sql build in function "GETDATE()" to Get the current Date
I can't figure out the magic words to allow posting JSON for a DateTime field in my app. When queried, DateTimes are returned as microseconds since the epoch. When I try to post in that format though ({"started":"1341006642000","task":{"id":1}}), I get "Invalid value: started".
I also tried adding #play.data.format.Formats.DateTime(pattern="yyyy-MM-dd HH:mm:ss") to the started field and posting {"started":"2012-07-02 09:24:45","task":{"id":1}} which had the same result.
The controller method is:
#BodyParser.Of(play.mvc.BodyParser.Json.class)
public static Result create(Long task_id) {
Form<Run> runForm = form(Run.class).bindFromRequest();
for (String key : runForm.data().keySet()) {
System.err.println(key + " => " + runForm.apply(key).value() + "\n");
}
if (runForm.hasErrors())
return badRequest(runForm.errorsAsJson());
Run run = runForm.get();
run.task = Task.find.byId(task_id);
run.save();
ObjectNode result = Json.newObject();
result.put("id", run.id);
return ok(result);
}
I can also see from the output that the values are being received correctly. Anyone know how to make this work?
After reading the "Register a custom DataBinder" section of the Handling form submission page along with the Application global settings page and comparing with this question I came up with the following solution:
I created a custom annotation with an optional format attribute:
package models;
import java.lang.annotation.*;
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#play.data.Form.Display(name = "format.joda.datetime", attributes = { "format" })
public #interface JodaDateTime {
String format() default "";
}
and registered a custom formatter from onStart:
import java.text.ParseException;
import java.util.Locale;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import play.*;
import play.data.format.Formatters;
public class Global extends GlobalSettings {
#Override
public void onStart(Application app) {
Formatters.register(DateTime.class, new Formatters.AnnotationFormatter<models.JodaDateTime,DateTime>() {
#Override
public DateTime parse(models.JodaDateTime annotation, String input, Locale locale) throws ParseException {
if (input == null || input.trim().isEmpty())
return null;
if (annotation.format().isEmpty())
return new DateTime(Long.parseLong(input));
else
return DateTimeFormat.forPattern(annotation.format()).withLocale(locale).parseDateTime(input);
}
#Override
public String print(models.JodaDateTime annotation, DateTime time, Locale locale) {
if (time == null)
return null;
if (annotation.format().isEmpty())
return time.getMillis() + "";
else
return time.toString(annotation.format(), locale);
}
});
}
}
You can specify a format if you want, or it will use milliseconds since the epoch by default. I was hoping there would be a simpler way since Joda is included with the Play distribution, but this got things working.
Note: you'll need to restart your Play app as it doesn't seem to detect changes to the Global class.
I am developing a web application with C# & SQL Server 2008.
I have a data reader which reads the column PlayTime, defined as TIME datatype.
I want to write a function that returns PlayTime's value.
private static Timespan GetTime(IDataReader rdr, string columnName)`
{
int index = rdr.GetOrdinal(columnName);
if (rdr.IsDBNull(index))
{
return ; // Here I want to return null or zero
}
return (TimeSpan)rdr[index];
}
Am I right using Timespan for time data type?
How to return null if datareader value is nothing?
Best Regards,
RedsDevils
Something like this:
private static TimeSpan? GetTime(IDataReader rdr, string columnName)
{
int index = rdr.GetOrdinal(columnName);
if (rdr.IsDBNull(index))
{
return null;
}
return (TimeSpan)rdr[index];
}
You need to use nullable Timespan
private static Nullable<TimeSpan> GetTime(IDataReader rdr, string columnName)
{
int index = rdr.GetOrdinal(columnName);
if (rdr.IsDBNull(index))
{
return null;
}
return (Nullable<TimeSpan>)rdr[index];
}