I have the following mapping for my table in MySql:
<class name="Tag, namespace" table="tags" >
<id name="id" type="Int32" unsaved-value="0">
<generator class="native"></generator>
</id>
<property name="name" type="String" not-null="true"></property>
<component name="record_dates" class="DateMetaData, namespace" >
<property name="created_at" type="DateTime" not-null="true"></property>
<property name="updated_at" type="DateTime" not-null="true"></property>
</component>
</class>
As you see the record_dates property is defined as a component field of type DateMetaDate. Both created_at and updated_at fields in 'tags' table are updated via triggers. Thus I can insert a new record like such:
var newTag = new Tag()
{
name = "some string here"
}
Int32 id = (Int32)Session.Save(tag);
Session.Flush();
ITag t = Session.Get<Tag>(id);
ViewData["xxx"] = t.name; // -----> not null
ViewData["xxx"] = t.record_dates.created_at; // -----> is null
However when querying the same record back immediately after it was inserted the record_dates field ends up null even though in the table those fields have got values.
Can any one please point out why the Session.Get ignores getting everything back from the table? is it because it caches the newly created record for which the records_dates is null? If so how can it be told to ignore the cached version?
Try using lazy="false" in your class tag:
<class name="Tag, namespace" table="tags" lazy="false">
As per my knowledge, the default behavior is to have lazy loading, which might not refresh your objects as needed.
The only I so far have found is to call Session.Clear() method on the NHibernate session object, which I guess forces NHibernate to fetch the record from the table again. But I'm afraid it removes everything in cache and thus be inefficient.
You can call ISession.Refresh(obj) to force the object to be reloaded from the database. My understanding is that this will not refresh all relationships, so if you need to reload the complete object graph, call ISession.Evict(obj) then ISession.Get(id) to remove it from the cache and reload it.
Related
I ran into .NET (Framework, w/ WinForm and WebForms) / MS SQL project where significant tables in the database contain a TIMESTAMP (aka ROWVERSION) column (called tsModified) to prevent concurrency issues. Additionally, this project does not allow the application layer to interact directly with the tables (instead all CRUD and business logic must be done through Stored Procedures).
One of the things that has been driving me crazy is how to use an SqlDataSource that can account for the TIMESTAMP column during UPDATE.
The basic form of the CRUD procs are like this:
CREATE PROC Customer_List
#pk_Customer INT = null
SELECT id, name, tsModified
FROM Customer
WHERE #pk_Customer IS NULL OR #pk_Customer = id;
CREATE PROC Customer_Update
#id INT,
#name varchar,
#tsMod TIMESTAMP
IF NOT EXISTS (SELECT TOP 1 1 FROM Customer where id=#id and tsModified=#tsModified)
Return; --actually RAISEERROR w/ a concurrency alert telling the user to refresh & try again
UPDATE Customer SET [param/value pairs] WHERE id = #id;
Sure, you could manually define partial classes and methods to account for tsModified, and then use asp:ObjectDataSource, but that is extra work. I just want the convenience of dropping an asp:SqlDataSource on the form, and get on with my day.
BUT... the SqlDataSource does not like the TIMESTAMP as a parameter. In fact, I've literally spent days researching how to make this work, and ran into plenty of others having the same issue.
I finally figured it out. See answer below.
Here's how you can use a MS SQL ROWVERSION (TIMESTAMP) column with an asp:SqlDataSource while using Stored Procedures, to handle concurrency.
Set up your SqlDataSource like this:
<asp:SqlDataSource ID="dsRegs" runat="server" OnUpdating="dsRegs_Updating" ConnectionString="[your connstr]" InsertCommand="RegulatoryAgency_Insert" InsertCommandType="StoredProcedure" SelectCommand="RegulatoryAgency_List" SelectCommandType="StoredProcedure" UpdateCommand="RegulatoryAgency_Update" UpdateCommandType="StoredProcedure">
<InsertParameters>
<asp:Parameter Name="RegulatoryCode" Type="String" />
<asp:Parameter Name="RegulatoryName" Type="String" />
<asp:Parameter Name="RegulatoryState" Type="String" />
</InsertParameters>
<SelectParameters>
<asp:Parameter Name="pk_RegulatoryAgency" Type="DBNull" />
</SelectParameters>
<UpdateParameters>
<asp:Parameter Name="pk_RegulatoryAgency" Type="Int32" />
<asp:Parameter Name="RegulatoryCode" Type="String" />
<asp:Parameter Name="RegulatoryName" Type="String" />
<asp:Parameter Name="RegulatoryState" Type="String" />
<asp:Parameter Direction="InputOutput" Name="tsModified" Type="Empty" />
</UpdateParameters>
</asp:SqlDataSource>
The important things to notice are:
In UpdateParameters, tsModified is the TIMESTAMP value and the Type="Empty".
OnUpdating is set to the dsRegs_Updating event.
Now the code behind:
/// <summary>
/// When editing for this record/row begins in the grid, we need to get the primary key from the row,
/// and then stuff the TIMESTAMP (tsModified) into a Session variable so it persists
/// </summary>
protected void gvRegs_StartRowEditing(object sender, DevExpress.Web.Data.ASPxStartRowEditingEventArgs e)
{
int pk = (int)e.EditingKeyValue;
var db = new myDataContext();
var ra = db.RegulatoryAgency_List(pk).First();
Session["tsModified"] = ra.tsModified;
}
/// <summary>
/// Before we call the database, convert the Session var back the original Linq-to-SQL type (System.Data.Linq.Binary), then
/// convert it to a (byte) array, and update the SqlDataSource parameter with the correct value.
/// </summary>
protected void dsRegs_Updating(object sender, SqlDataSourceCommandEventArgs e)
{
DbParameter dp = e.Command.Parameters["#tsModified"];
dp.Value = ((System.Data.Linq.Binary)Session["tsModified"]).ToArray();
}
In this example, the front is using a DevExpress ASPxGridView, but the databinding and events should be similar on other databound controls. When the row editing begins, we pull the tsModified value for the record from the database and place it into a Session variable. Then the SqlDataSource fires its Updating event, we grab the Session variable, convert it back to it's original format (in my case a System.Data.Linq.Binary because this example is using Linq-to-SQL), and finally the last trick is that you can't pass the TIMESTAMP value as binary, varbinary, or byte -- is must be sent as btye[], which the .ToArray() is taking care.
With the code like this, I'm able to successfully SELECT, INSERT, UPDATE and DELETE through the SqlDataSource, and the tsModified (TIMESTAMP) value in the database increments as expected.
I'm trying to get a simple 1-N FK relationship working with DataNucleus JDO. I have classes GridDO and GridColumnDO with relevant getters and setters. I'm trying to establish that a grid has multiple columns. The entity_attribute (GridColumn) table has a FK column (named entity_id) to the entity (Grid) table's PK (also named entity_id). I worked off of the example code on the DataNucleus website. I can load the GridDO object, but when I try to get the columns, I get
Iteration request failed : SELECT 'com.mycompany.myapplication.data.GridColumnDO' AS NUCLEUS_TYPE,A0.DISPLAY_NAME,A0.COLUMN_ORDER,A0.PROPERTY_NAME,A0.ENTITY_ID,A0.ENTITY_ATTRIBUTE_ID,A0.VALUE_TYPE FROM ENTITY_ATTRIBUTE A0 WHERE A0.ENTITY_ID = ?
org.datanucleus.exceptions.NucleusDataStoreException: Iteration request failed : SELECT 'com.mycompany.myapplication.data.GridColumnDO' AS NUCLEUS_TYPE,A0.DISPLAY_NAME,A0.COLUMN_ORDER,A0.PROPERTY_NAME,A0.ENTITY_ID,A0.ENTITY_ATTRIBUTE_ID,A0.VALUE_TYPE FROM ENTITY_ATTRIBUTE A0 WHERE A0.ENTITY_ID = ?
...
Caused by: java.sql.SQLException: Parameter #1 has not been set.
Does anybody know what I might be doing wrong? I've been searching and banging at this for a while now, with no real luck. This is an excerpt from package-mssql.orm:
<class name="GridDO" identity-type="application" table="entity">
<field name="id" primary-key="true">
<column name="entity_id"/>
</field>
<field name="columns">
<collection element-type="com.mycompany.myapplication.data.GridColumnDO"/>
<element column="entity_id"/>
</field>
...
</class>
<class name="GridColumnDO" identity-type="application" table="entity_attribute">
<field name="id" primary-key="true">
<column name="entity_attribute_id"/>
</field>
...
</class>
The issue was that PersistenceCapable and PrimaryKey must either both be identified via annotations or both be identified via metadata. I had PersistenceCapable in an annotation and PrimaryKey in my .orm file.
I have added document library template using VS 2012. and then added custom content types to this library. the content types includes 3 managed metadata fields. After deploying the project when I add terms to taxonomy fields in document library, I got following error:
"The SPListItem being updated was not retrieved with all taxonomy fields"
I could not find any solution. Any body have an idea whats wrong ?
In the ELEMENTS.XML file of the contenttype your field (actually you need TWO, note how they are linked togheter) must be something like:
<Field Type="Note"
ID="{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}"
DisplayName="_Circular_Tags"
Name="Circular_Tags_NOTE"
StaticName="Circular_Tags_NOTE"
ShowInViewForms="FALSE"
Required="FALSE"
Hidden="TRUE"
CanToggleHidden="TRUE"
RowOrdinal="0">
</Field>
<Field Type="TaxonomyFieldTypeMulti"
ID="{DF553026-F699-456F-AA24-0C6087DBE885}"
Name="Circular_Tags"
StaticName="Circular_Tags"
DisplayName="Circular_Tags_DisplayName"
Description="Circular_Tags_Description"
ShowField="Circular_Tags_Path"
Required="FALSE"
Sortable="FALSE"
AllowDeletion="TRUE"
EnforceUniqueValues="FALSE"
ShowInViewForms="TRUE"
Group="MyContentTypes_Group">
<Default></Default>
<Customization>
<ArrayOfProperty>
<Property>
<Name>TextField</Name>
<Value xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}</Value>
</Property>
</ArrayOfProperty>
</Customization>
</Field>
Then you need (in the feature-activated code) do the following:
SPSite site = properties.Feature.Parent as SPSite;
Guid fieldId = new Guid("{DF553026-F699-456F-AA24-0C6087DBE885}");
if (site.RootWeb.Fields.Contains(fieldId))
{
TaxonomySession session = new TaxonomySession(site);
if (session.TermStores.Count != 0)
{
var termStore = session.TermStores["ManagedMetadata_Proxy"];
var group = termStore.Groups["GroupName"];
var termSet = group.TermSets["TermSetName"];
TaxonomyField field = site.RootWeb.Fields[fieldId] as TaxonomyField;
//set the text field to the id of the _Circular_Tags field : 4B53F593-CF60-40DF-AEAF-23155BB9AA3F
field.TextField = new Guid("{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}");
// Connect to MMS
field.SspId = termSet.TermStore.Id;
field.TermSetId = termSet.Id;
field.TargetTemplate = string.Empty;
field.AnchorId = Guid.Empty;
field.Update();
}
}
Finally in the SCHEMA.XML file in the List definition you should have a definition like this for the field:
<Field Type="TaxonomyFieldType" ID="{DF553026-F699-456F-AA24-0C6087DBE885}" Name="Circular_Tags" StaticName="Circular_Tags" DisplayName="Circular_Tags_DisplayName" Description="Circular_Tags_Description" ShowField="Circular_Tags_Path" Mult="TRUE" Required="FALSE" Sortable="FALSE" AllowDeletion="TRUE" EnforceUniqueValues="FALSE" ShowInViewForms="TRUE" Group="ContentTypes_Group">
<Default></Default>
<Customization>
<ArrayOfProperty>
<Property>
<Name>TextField</Name>
<Value xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{4B53F593-CF60-40DF-AEAF-23155BB9AA3F}</Value>
</Property>
</ArrayOfProperty>
</Customization>
</Field>
I am working under a web application which use spring mvc + mybatis + mysql.
And I found that I can not get the auto-generated key for the last inserted record (I have googled so much).
This is the related configuration(take the model 'Post' for example):
spring.xml:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
//omitted
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="config.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
mybatis config.xml
<configuration>
<typeAliases>
<typeAlias alias="Post" type="com.king.model.Post" />
</typeAliases>
<mappers>
<mapper resource="com/king/model/PostMapper.xml" />
</mappers>
</configuration>
PostMapper.xml:
<mapper namespace="com.king.model.PostMapper">
<insert id="insert" parameterType="Post">
insert into posts (title,body,created_at,updated_at) values (#{title},#{body},#{createDate},#{updateDate})
</insert>
</mapper>
Dao:
public abstract class AbstractSimpleDaoImpl<T> extends SqlSessionDaoSupport{
#Override
public int add(T entity) {
return getSqlSession().insert(getMapperNamespace() + ".insert", entity);
}
protected abstract String getMapperNamespace();
}
public class PostDao extends AbstractSimpleDaoImpl<Post> {
#Override
protected String getMapperNamespace() {
return "com.king.model.PostMapper";
}
}
However,when I insert a new Post to database:
postDao.add(post);
I can not get the generated id for the inserted post. I always get 1. I know this is the row-affected number.
Also,I read the mybatis guide,and I tried this:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id">
But it seems that,this does not work.
How to fix it?
In fact the following works:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id">
postDao.add(post);
will return 1 as before,but post.getId() will get the key.
This can be done using annotations as well.
final String INSERT = "insert into posts (title,body,created_at,updated_at) values (#{title},#{body},#{createDate},#{updateDate})";
#Insert(INSERT)
#Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
void insert(Post post) throws Exception;
Set keyProperty as the Java variable name.
Set keyColumn as the column name in the database.
After insert, post.getId() will have your generated ID.
Try using the following code; I added the keyColumn field to your code:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
Alternatively, if you are using PostgreSql:
<insert id="insert" parameterType="Post" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
<selectKey keyProperty="id" resultType="java.lang.Integer">
SELECT
currVal('your_tbl_seq') as id
</selectKey> </insert>
This will work for Spring Boot using annotations and getting the id as a return value as in your original question.
#Insert("insert into sources (host, created_at) values (#{host}, now())")
#SelectKey(statement="select LAST_INSERT_ID()", keyProperty="id", before=false, resultType=long.class)
long create(#Param("host") String host);
Then long id = sourceMapper.create("abc"); would return your primary key.
I have a StoredProcedure called "usp_posts_getall" and it has 1 parameter called "#thisCategoryID"
in my "thisCategoryID", any values other than 0(zero) will return all the records in my Posts table.
Now I have a category menu items and each time I select, I set the value in my Session name called "SelectedCID".
So, How do I ...
Create a SessionParameter Programmatically in SqlDataSource?
UPDATE:
ok. I got it working now.
If it's a session parameter that's used by the SqlDataSource, then you can set the value in the session, e.g in Page_Load():
Session["thisCategoryID"] = theCategoryId;
(am I misunderstanding the question?)
Ok, update:
I think you can create an event handler for the SqlDataSource.OnSelecting event. In that handler, you can access the Parameters collection of the datasource and can add another Parameter to it. I currently cannot test the following code, so it might not be fully correct, but I hope you see the idea:
SqlDataSource1_OnSelecting(SqlDataSourceSelectingEventArgs args)
{
var param = new Parameter("#thisCatagoryID");
param.DefaultValue = Session["SelectedCID"];
SqlDataSource1.SelectParameters.Add(param);
}
Alternatively, you can set the parameter declaratively in the markup, e.g:
<asp:SqlDataSource ...>
<SelectParameters>
<asp:SessionParameter Name="thisCategoryID" SessionField="SelectedCID"
DefaultValue="0" />
...
</SelectParameters>
</asp:SqlDataSource>