Can we check double creation of a state in Corda? - blockchain

In my use case, I have a student enrolling to a specific program which in turn creates a student state on the ledger.
Now, if the same student enrolls with same credentials again, I want to avoid it and throw some exception or message.
One solution, I can think of is, I can query the vault before creating the student state and if that student is not found in the ledger than only he is allowed to enroll.
But, this seems like a vague idea.
Can someone provide a better approach or some other way I am not aware of?

You should implement schema for your state as in this example:
class CashState(
val owner: AbstractParty,
val pennies: Long) : ContractState, QueryableState {
override val participants get() = listOf(owner)
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is CashSchemaV1 -> CashSchemaV1.PersistentCashState(
this.owner,
this.pennies
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(CashSchemaV1)
}
The schema itself:
object CashSchema
#CordaSerializable
object CashSchemaV1 : MappedSchema(schemaFamily = CashSchema.javaClass, version = 1, mappedTypes = listOf(PersistentCashState::class.java)) {
#Entity
#Table(name = "contract_cash_states")
class PersistentCashState(
#Column(name = "owner_name", unique=true, nullable = true)
var owner: AbstractParty?,
#Column(name = "pennies", nullable = false)
var pennies: Long
) : PersistentState()
}
The key point is to make the columns unique, so when you add duplicate value the exception is thrown.
docs

Related

performance implications of holding state in large data classes and updating it using copy()

In order to store state in jetpack compose I have so far used the the following pattern:
private val _largeDataClass:MutableState<LargeDataClass> = mutableStateOf(LargeDataClass())
val largeDataClass :State<LargeDataClass> = _largeDataClass
then I display some or all of the properties of this class in my composition. When the user changes a property of this data class I need to update the state and I do it in the following way:
fun onUserEvent(somePropertyChange:String){
_largeDataClass.value=largeDataClass.value.copy(someProperty = somePropertyChange)
}
I got this approach from the following post. It works and has the benefit of keeping my codebase relatively small (as there might be 20+ different properties in LargeDataClass that I dont need to declare individually as mutable state) BUT, if I am not mistaken, following this approach will trigger the recomposition of my entire screen even if the user just types a single letter in one of my many TextFields. As all my composables display some property of largeDataClass and they have just been notified that its value has changed.
My first question is wether I am right in this last assumption. Will my current way of holding state negatively affect my apps performance because I am forcing the screen to redraw itself completely constantly? Or are there some optimizations, that I'm unaware of, in compose that prevent this from happening and render my appoach safe?
my second question: I would really love it if there was a way of turning a data class, say:
data class Student(
val key: String = "s0",
val firstName: String = "",
val lastName: String = "")
into an equivalent state holder class (something similar to the following)
class StudentState(s:Student){
val key= mutableStateOf(s:Key),
val firstName= mutableStateOf(s.firstName),
val lastName= mutableStateOf(s.lastName)}
(Ideally without having to explicitly write such a class myself every time) Does this exist already? is there a way of using reflection or the like to achieve this for a generic data class?
I am still learning to deal with state in jetpack compose and I want to get it right. It seems to me that tracking the properties of my data classes individually either in the ViewModel or in a State Holder class is the right thing to do, but on the other hand this makes my code a lot longer and it just feels like I am doing a lot of stuff twice and my code becomes less readable and maintainable. Any insights are much appreciated
You could use reflection to create mutableStates for the members of any class like so:
fun createStateMap(baseObject: Any) = with(baseObject::class.memberProperties) {
map { it.name }.zip(map { mutableStateOf(it.getter.call(baseObject)) }).toMap()
}
And then use it like this:
class MyViewModel : ViewModel() {
private val student = Student(firstName = "John", lastName = "Doe")
val studentStateMap = createStateMap(student)
}
#Composable
fun MyComposable(viewModel: MyViewModel) {
val student = viewModel.studentStateMap
Button(
onClick = { student["firstName"]?.value = "Jack" }
) {
Text(text = student["firstName"]?.value.toString())
}
}
I wouldn't use it, it's not typesafe, it's messy and ugly, but it's possible.
An Annotation to store data class #AsState
Well I am still not sure about wether it is fine to simply .copy(changedValue = "...") a large data class or if this is inefficient because it triggers unecessary recompositions.
In any case just to be safe (and cause I think its cleaner) I wrote an AnnotationProcessor that takes my data classes and creates both a mutable and immutable version of the class holding all properties as state. It supports both lists and maps but is shallow (meaning that it wont repeat the same process for nested classes). Here an example of the class with the annotation and the result.
please let me know if you consider this to be usefull in order to track state more cleanly when a data class is displayed/edited in a composable (and also if you dont)
The original class
#AsState
data class Test(val name:String, val age:Int, val map:HashMap<String,Int>, val list:ArrayList<String>)
The mutable verion of the class with a custonm constructor and rootClass getter
public class TestMutableState {
public val name: MutableState<String>
public val age: MutableState<Int>
public val map: SnapshotStateMap<String, Int>
public val list: SnapshotStateList<String>
public constructor(rootObject: Test) {
this.name=mutableStateOf(rootObject.name)
this.age=mutableStateOf(rootObject.age)
this.map=rootObject.map.map{Pair(it.key,it.value)}.toMutableStateMap()
this.list=rootObject.list.toMutableStateList()
}
public fun getTest(): Test = Test(name = this.name.value,
age = this.age.value,
map = HashMap(this.map),
list = ArrayList(this.list),
)
}
The immutable version that can be public in the ViewModel
public class TestState {
public val name: State<String>
public val age: State<Int>
public val map: SnapshotStateMap<String, Int>
public val list: SnapshotStateList<String>
public constructor(mutableObject: TestMutableState) {
this.name=mutableObject.name
this.age=mutableObject.age
this.map=mutableObject.map
this.list=mutableObject.list
}
}
TL;DR
Next I am pasting the source code for my annotation processor so you can implement it. I basically followed this article and implemented some of my own changes based on arduous googling. I might make this a module in the future so that other people can more easily implement this in their projects i there is any interest:
Annotation class
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.SOURCE)
public annotation class AsState
Annotation processor
#AutoService(Processor::class)
class AnnotationProcessor : AbstractProcessor() {
companion object {
const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"
}
override fun getSupportedAnnotationTypes(): MutableSet<String> {
return mutableSetOf(AsState::class.java.name)
}
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(AsState::class.java)
.forEach {
if (it.kind != ElementKind.CLASS) {
processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "Only classes can be annotated")
return true
}
processAnnotation(it)
}
return false
}
#OptIn(KotlinPoetMetadataPreview::class, com.squareup.kotlinpoet.DelicateKotlinPoetApi::class)
private fun processAnnotation(element: Element) {
val className = element.simpleName.toString()
val pack = processingEnv.elementUtils.getPackageOf(element).toString()
val kmClass = (element as TypeElement).toImmutableKmClass()
//create vessel for mutable state class
val mutableFileName = "${className}MutableState"
val mutableFileBuilder= FileSpec.builder(pack, mutableFileName)
val mutableClassBuilder = TypeSpec.classBuilder(mutableFileName)
val mutableConstructorBuilder= FunSpec.constructorBuilder()
.addParameter("rootObject",element.asType().asTypeName())
var helper="return ${element.simpleName}("
//create vessel for immutable state class
val stateFileName = "${className}State"
val stateFileBuilder= FileSpec.builder(pack, stateFileName)
val stateClassBuilder = TypeSpec.classBuilder(stateFileName)
val stateConstructorBuilder= FunSpec.constructorBuilder()
.addParameter("mutableObject",ClassName(pack,mutableFileName))
//import state related libraries
val mutableStateClass= ClassName("androidx.compose.runtime","MutableState")
val stateClass=ClassName("androidx.compose.runtime","State")
val snapshotStateMap= ClassName("androidx.compose.runtime.snapshots","SnapshotStateMap")
val snapshotStateList=ClassName("androidx.compose.runtime.snapshots","SnapshotStateList")
fun processMapParameter(property: ImmutableKmValueParameter) {
val clName =
((property.type?.abbreviatedType?.classifier) as KmClassifier.TypeAlias).name
val arguments = property.type?.abbreviatedType?.arguments?.map {
ClassInspectorUtil.createClassName(
((it.type?.classifier) as KmClassifier.Class).name
)
}
val paramClass = ClassInspectorUtil.createClassName(clName)
val elementPackage = clName.replace("/", ".")
val paramName = property.name
arguments?.let {
mutableClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateMap.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
arguments?.let {
stateClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateMap.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
helper = helper.plus("${paramName} = ${paramClass.simpleName}(this.${paramName}),\n")
mutableConstructorBuilder
.addStatement("this.${paramName}=rootObject.${paramName}.map{Pair(it.key,it.value)}.toMutableStateMap()")
stateConstructorBuilder
.addStatement("this.${paramName}=mutableObject.${paramName}")
}
fun processListParameter(property: ImmutableKmValueParameter) {
val clName =
((property.type?.abbreviatedType?.classifier) as KmClassifier.TypeAlias).name
val arguments = property.type?.abbreviatedType?.arguments?.map {
ClassInspectorUtil.createClassName(
((it.type?.classifier) as KmClassifier.Class).name
)
}
val paramClass = ClassInspectorUtil.createClassName(clName)
val elementPackage = clName.replace("/", ".")
val paramName = property.name
arguments?.let {
mutableClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateList.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
arguments?.let {
stateClassBuilder.addProperty(
PropertySpec.builder(
paramName,
snapshotStateList.parameterizedBy(it), KModifier.PUBLIC
)
.build()
)
}
helper = helper.plus("${paramName} = ${paramClass.simpleName}(this.${paramName}),\n")
mutableConstructorBuilder
.addStatement("this.${paramName}=rootObject.${paramName}.toMutableStateList()")
stateConstructorBuilder
.addStatement("this.${paramName}=mutableObject.${paramName}")
}
fun processDefaultParameter(property: ImmutableKmValueParameter) {
val clName = ((property.type?.classifier) as KmClassifier.Class).name
val paramClass = ClassInspectorUtil.createClassName(clName)
val elementPackage = clName.replace("/", ".")
val paramName = property.name
mutableClassBuilder.addProperty(
PropertySpec.builder(
paramName,
mutableStateClass.parameterizedBy(paramClass), KModifier.PUBLIC
).build()
)
stateClassBuilder.addProperty(
PropertySpec.builder(
paramName,
stateClass.parameterizedBy(paramClass),
KModifier.PUBLIC
).build()
)
helper = helper.plus("${paramName} = this.${paramName}.value,\n")
mutableConstructorBuilder
.addStatement(
"this.${paramName}=mutableStateOf(rootObject.${paramName}) "
)
stateConstructorBuilder
.addStatement("this.${paramName}=mutableObject.${paramName}")
}
for (property in kmClass.constructors[0].valueParameters) {
val javaPackage = (property.type!!.classifier as KmClassifier.Class).name.replace("/", ".")
val javaClass=try {
Class.forName(javaPackage)
}catch (e:Exception){
String::class.java
}
when{
Map::class.java.isAssignableFrom(javaClass) ->{ //if property is of type map
processMapParameter(property)
}
List::class.java.isAssignableFrom(javaClass) ->{ //if property is of type list
processListParameter(property)
}
else ->{ //all others
processDefaultParameter(property)
}
}
}
helper=helper.plus(")") //close off method
val getRootBuilder= FunSpec.builder("get$className")
.returns(element.asClassName())
getRootBuilder.addStatement(helper.toString())
mutableClassBuilder.addFunction(mutableConstructorBuilder.build()).addFunction(getRootBuilder.build())
stateClassBuilder.addFunction(stateConstructorBuilder.build())
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
val mutableFile = mutableFileBuilder
.addImport("androidx.compose.runtime", "mutableStateOf")
.addImport("androidx.compose.runtime","toMutableStateMap")
.addImport("androidx.compose.runtime","toMutableStateList")
.addType(mutableClassBuilder.build())
.build()
mutableFile.writeTo(File(kaptKotlinGeneratedDir))
val stateFile = stateFileBuilder
.addType(stateClassBuilder.build())
.build()
stateFile.writeTo(File(kaptKotlinGeneratedDir))
}
}
gradle annotation
plugins {
id 'java-library'
id 'org.jetbrains.kotlin.jvm'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
gradle processor
plugins {
id 'kotlin'
id 'kotlin-kapt'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':annotations')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10"
// https://mvnrepository.com/artifact/com.squareup/kotlinpoet
implementation 'com.squareup:kotlinpoet:1.10.2'
implementation "com.squareup:kotlinpoet-metadata:1.7.1"
implementation "com.squareup:kotlinpoet-metadata-specs:1.7.1"
implementation "com.google.auto.service:auto-service:1.0.1"
// https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-metadata-jvm
implementation "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.2"
implementation 'org.json:json:20211205'
kapt "com.google.auto.service:auto-service:1.0.1"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

How to put multiple objects in a mutableListOf in Kotlin?

I'm trying to put some objects in a mutableListOf, but all of them ended up overriding.
class Users {...}
class dbUsers{
var users = Users()
val list = mutableListOf(users)
fun addUser(phone: Int, name: String){
users.phone = phone
users.name = name
list.add(users)
}
}
fun main() {
var dbAccess = dbUsers()
dbAccess.addUser(8439, "Thiago")
dbAccess.addUser(12312, "Maria")
println(dbAccess.list[0].name)
println(dbAccess.list[1].name)
}
When We print at position 1, we see that was override
You have initialized Users object only once, at the class level in dbUsers. You need to create new object everytime before adding the user to the list. Move the users initialization within the method instead
class dbUsers{
val list = mutableListOf<Users>()
fun addUser(phone: Int, name: String){
var users = Users()
users.phone = phone
users.name = name
list.add(users)
}
}
It looks like the list is referencing from users and since users itself is updated every time, you get this results.
There are two ways you can approach this issue ,
Creating a new user every-time calling addUser , this is easy
If you have other data in Users than name and phone , you can just make a DeepCopy of your object by add in a new function in your class copy() like
class YourClass () {
// Your class other stuffs here
fun copy(): YourClass { //Get another instance of YourClass with the values like this!
val json = Gson().toJson(this)
return Gson().fromJson(json, YourClass::class.java)
}
}
then by using yourClassObject.copy() you will get a new instance with same values
fun addUser(phone: Int, name: String){
val newUsers = users.copy()
newUsers.phone = phone
newUsers.name = name
list.add(newUsers)
}

JPA Cascading removal of parent and Child

I'm trying to learn and understand JPA, and just have a couple of questions regarding deleting a parent and its children in one go. I'm using OpenJPA and EJB3. I have two entities, a Category and a Product. A Category contains many products and a product has a reference to its parent category. The category's list of products is set to cascade.
//Category
#Entity #NamedQueries({#NamedQuery(name = "Category.getCategoryByName", query = "SELECT c FROM Category c WHERE c.name = :name"),#NamedQuery(name = "Category.getCategoryByCategoryId", query = "SELECT c FROM Category c WHERE c.categoryid = :categoryid"), #NamedQuery(name = "Category.getAllCategories", query = "SELECT c FROM Category c left join fetch c.products")})
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=IDENTITY)
private Integer categoryid;
private String name;
//bi-directional many-to-one association to Product
#OneToMany(cascade={CascadeType.ALL}, orphanRemoval = true,
fetch = EAGER, mappedBy="category")
private List<Product> products;
}
//Product
#Entity
#NamedQueries({#NamedQuery(name = "Product.getProductsByCategory",
query = "SELECT p.code, p.description, p.name, p.productid, p.weight FROM Product p WHERE p.category.categoryid = :category_categoryid"),
#NamedQuery(name = "Product.getProductByName", query = "SELECT p FROM Product p WHERE p.name = :name"),
#NamedQuery(name = "Product.getProductByCode", query = "SELECT p FROM Product p WHERE p.code = :code"),
#NamedQuery(name = "Product.getProductByProductId", query = "SELECT p FROM Product p WHERE p.productid = :productid"),
#NamedQuery(name = "Product.getAllProducts", query = "SELECT p FROM Product p")})
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=IDENTITY)
private Integer productid;
private String code;
private String description;
private String name;
private Double weight;
//bi-directional many-to-one association to Category
#ManyToOne(optional = false)
#JoinColumn(name="CATEGORYID")
private Category category;
}
}
// The EJB
#Stateless
#LocalBean
public class ShopManagerBean implements Serializable {
#PersistenceContext(unitName = "TestEJBProject2", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteCategory(Category category)
throws TestApplicationException {
try {
Category actualCat = entityManager.find(Category.class,
category.getCategoryid());
List<Product> products = actualCat.getProducts();
if (products != null) {
Iterator<Product> it = products.iterator();
while (it.hasNext()) {
Product p = it.next();
it.remove();
entityManager.remove(p);
}
}
entityManager.refresh(actualCat);
entityManager.remove(actualCat);
} catch (Exception e) {
e.printStackTrace();
throw new TestApplicationException("Error creating new Product", e);
}
}
}
If I use the following code in the deleteCategory method the EJB then I cannot delete the parent and children as I get an Optimistic Locking exception (An optimistic lock violation was detected when flushing object instance "entity.Product-101" to the data store. This indicates that the object was concurrently modified in another transaction.) - complaining about flushing the product child to the data store
Category actualCat = entityManager.find(Category.class, category.getCategoryid());
if (products != null) {
actualCat.getProducts().clear();
}
entityManager.remove(actualCat);
However, if I use the following code in the deleteCategory method then I can delete the parent and children...but only if I call entityManager.refresh(actualCat) after removing the children and before removing the parent (otherwise I get an optimistic locking exception). Could somebody please explain to me why this is the case and also what the correct/best way of doing a cascading delete with OpenJPA V2 would be?
Category actualCat = entityManager.find(Category.class, category.getCategoryid());
List<Product> products = actualCat.getProducts();
if (products != null) {
Iterator<Product> it = products.iterator();
while (it.hasNext()) {
Product p = it.next();
it.remove();
entityManager.remove(p);
}
}
entityManager.refresh(actualCat);
entityManager.remove(actualCat);
Thanks in advance for your help
Fais
Addition
Here is the db creation script:
--
CREATE SCHEMA "DB2ADMIN";
CREATE TABLE "DB2ADMIN"."CATEGORY" (
"CATEGORYID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20),
"NAME" VARCHAR(50) NOT NULL
)
DATA CAPTURE NONE;
CREATE TABLE "DB2ADMIN"."PRODUCT" (
"PRODUCTID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20),
"CODE" CHAR(15) NOT NULL,
"NAME" VARCHAR(50) NOT NULL,
"DESCRIPTION" VARCHAR(200) NOT NULL,
"WEIGHT" FLOAT(53) NOT NULL,
"CATEGORYID" INTEGER NOT NULL
)
DATA CAPTURE NONE;
ALTER TABLE "DB2ADMIN"."CATEGORY" ADD CONSTRAINT "CATEGORY_PK" PRIMARY KEY
("CATEGORYID");
ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_PK" PRIMARY KEY
("PRODUCTID");
ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_CATEGORY_FK" FOREIGN KEY
("CATEGORYID")
REFERENCES "DB2ADMIN"."CATEGORY"
("CATEGORYID")
ON DELETE CASCADE;

JPA: persist does not insert into join table

All,
I am using JPA for this application and annotations for Mapping entities. I have an entity called UserStory and another one called Revision. There is a OneToMany for UserStory to Revision.
#Entity
#Table(name = "user_story")
#NamedNativeQueries({
#NamedNativeQuery(name = "storyBacklog", query = "SELECT userstory.rank AS rank, userstory.description AS description, userstory.estimate AS estimate, userstory.name AS name, "
+ "userstory.id AS id, userstory.status AS status FROM user_story userstory ORDER BY userstory.rank ASC", resultClass = UserStory.class),
#NamedNativeQuery(name = "getCos", query = "SELECT conditions.cos As cos FROM story_cos conditions WHERE conditions.story_id=?1", resultSetMapping = "cosMapping") })
#SqlResultSetMappings({ #SqlResultSetMapping(name = "cosMapping", columns = #ColumnResult(name = "cos")) })
public class UserStory implements Serializable {
private static final long serialVersionUID = 248298400283358441L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "story_revisions", joinColumns = #JoinColumn(name = "story_id"), inverseJoinColumns = #JoinColumn(name = "revision_id"))
private Set<Revision> revisions;
here's Revision entity:
#Entity
#Table(name = "revision")
public class Revision implements Serializable {
private static final long serialVersionUID = -1823230375873326645L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false)
private String description;
#Column(name = "date_created", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
When I create a userStory; I add a revision on to it,
but the join table is not populated unless, I persist story first,
then add revision and merge it.
here's the code for saving a UserStory:
public UserStory saveUserStory(UserStory userStory) {
Revision revision = new Revision();
revision.setCreationDate(new Timestamp(System.currentTimeMillis()));
revision.setDescription("User story created");
Set<Revision> revisions = new HashSet<Revision>();
revisions.add(revision);
userStory.setRevisions(revisions);
return storyDao.create(userStory);
}
in StoryDao I call the persist method:
#Transactional(readOnly = false)
public UserStory create(UserStory userStory) {
if (userStory.getRank() == null) {
Integer highestRank = 0;
highestRank = (Integer) entityManager.createNativeQuery("select max(rank) from user_story")
.getSingleResult();
if (highestRank != null)
highestRank += 1;
else
highestRank = new Integer(1);
userStory.setRank(highestRank);
}
entityManager.persist(userStory);
LOGGER.debug("Added User Story with id " + userStory.getId());
entityManager.detach(userStory);
return userStory;
}
here's the SQL from LOGS
Hibernate:
insert
into
user_story
(description, estimate, name, rank, status)
values
(?, ?, ?, ?, ?)
Hibernate:
insert
into
revision
(date_created, description)
values
(?, ?)
Hibernate:
select
revision0_.id as id5_0_,
revision0_.date_created as date2_5_0_,
revision0_.description as descript3_5_0_
from
revision revision0_
where
revision0_.id=?
Hibernate:
select
userstory0_.id as id3_1_,
userstory0_.description as descript2_3_1_,
userstory0_.estimate as estimate3_1_,
userstory0_.name as name3_1_,
userstory0_.rank as rank3_1_,
userstory0_.status as status3_1_,
revisions1_.story_id as story1_3_3_,
revision2_.id as revision2_3_,
revision2_.id as id5_0_,
revision2_.date_created as date2_5_0_,
revision2_.description as descript3_5_0_
from
user_story userstory0_
left outer join
story_revisions revisions1_
on userstory0_.id=revisions1_.story_id
left outer join
revision revision2_
on revisions1_.revision_id=revision2_.id
where
userstory0_.id=?
I can see from here it saves the user story and revision, but then tries to run a join to see if the relation exists before doing an insert into the join table. Which of course it will not find because I am creating this object.
How do it get the join table populated in this case?
Works now. Here's the updated code
revisions.add(revision);
userStory = storyDao.create(userStory);
userStory.setRevisions(revisions);
return storyDao.update(userStory);
I am still not sure why this is required; the two step method where I persist an object then update it.

Nhibernate and SQLite Exception "Cannot write to a Closed TextWriter"

I'm trying to use NHibernate with SQLite for Unit Testing. But I'm keep getting error
ADOException was unhandled by user code
While preparing INSERT INTO User (First_Name, Last_Name, UserName, Password, Email, Active, Default_Clinic_Identification_Number, Login_Icon, Created_Date, Created_By, Modified_Date, Modified_By) VALUES (#p0, #p1, #p2, #p3, #p4, #p5, #p6, #p7, #p8, #p9, #p10, #p11); select last_insert_rowid() an error occurred
Inner Exception: {"Cannot write to a closed TextWriter."}
I don't know what i'm doing wrong. Here is my code
public class InMemoryDatabaseTest : IDisposable
{
private static Configuration Configuration;
private static ISessionFactory SessionFactory;
protected ISession session;
public InMemoryDatabaseTest()
{
if (Configuration == null)
{
Assembly a = Assembly.Load("Astute.Framework.Data");
Configuration = new Configuration()
.SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.Dialect, typeof(SQLiteDialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionString, "data source=:memory:")
.SetProperty(Environment.ProxyFactoryFactoryClass, typeof(ProxyFactoryFactory).AssemblyQualifiedName)
.AddAssembly(a);
SessionFactory = Configuration.BuildSessionFactory();
}
session = SessionFactory.OpenSession();
new SchemaExport(Configuration).Execute(true, true, false, session.Connection, Console.Out);
}
public void Dispose()
{
session.Dispose();
}
}
[TestClass]
public class Test : InMemoryDatabaseTest
{
[TestMethod]
public void CanSaveUser()
{
object id;
using (var tx = session.BeginTransaction())
{
id = session.Save(new User
{
FirstName = "Imran",
LastName = "Ashraf",
UserName = "imran",
Password = "Test",
Email = "Test#test.com",
IsActive = true,
DefaultClinicIdentifcationNumber = "",
LoginIcon = "",
CreatedBy = 1000000,
CreatedDate = DateTime.Today,
ModifiedBy = 1000000,
ModifiedDate = DateTime.Today
});
tx.Commit();
}
session.Clear();
}
}
I'm getting error on this line id = session.Save. I got this example from http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx
Any idea?
Thanks in advance.
The error message "Inner Exception: {"Cannot write to a closed TextWriter."}" is given when you are trying to write into the Console.Out.
I created a new TextWriter and used it:
TextWriter writer = null;
new SchemaExport(Configuration).Execute(true, true, false, session.Connection, writer);
That fixed that exception.
I can't see the problem. I use this as connection string:
Data Source=:memory:;Version=3;New=True;
You may try it to see if it solves the problem. The New=True; part looks promising.
In my case I noticed that I used two instances of InMemoryDatabaseTest. I deleted one instance from the test.