Cascading delete with sqlite isn't working with a join table - go-gorm

If you have a table that's just a hash of a many-to-many relationship between two other tables, like :
type User struct{
UserId string
...
}
type Roles struct{
Level string
...
}
where UserId and Level are PrimaryKeys.
The "join table" was originally:
type User_Role struct{
UserId string
Level string
}
How can you build that in gorm with cascading foreign key relataionships, et al.
I've been trying to piece it together from their docs, and different sources, but when I delete either the user or role, the join table still has the records.
Currently, this is what I've got:
type User_Roles struct {
User_Id string `gorm:"primaryKey; not null;ForeignKey:user_id;references:users.user_id,constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
Level string `gorm:"primaryKey; not null;ForeignKey:level;references:roles.level,constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
When I had fields in the struct for User and Role sqlite created the table with every field from both, doing it this way created the table with the right fields, but the cascading isn't working.

Foreign key checks are disabled in SQLite by default, you need to enable them:
db.Exec("PRAGMA foreign_keys = ON")
If you specify the foreign key relations like this, they should work as you'd expect:
type User struct {
UserId string `gorm:"primaryKey;not null;"`
UserRoles []User_Roles `gorm:"foreignKey:UserId;references:UserId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}
type User_Roles struct {
UserId string `gorm:"primaryKey;not null;"`
Level string `gorm:"primaryKey;not null;"`
}
i.e. if you delete a user, its associations will be removed.

Related

Room insert into one-to-many relationship

When trying to use intermediary classes to model entity relationships in Room, I have run into an issue. Whilst documentation describes how to get from a one-to-many relationship, it does not describe how to insert.
I'm assuming this cannot be done automatically, therefore we need a query to insert the parent, retrieve the ID for the parent and assign it to the child's foreign key, and then insert the child.
The problem is that I am unsure where to put such a query. If I include it in my DAO, then I will have to include superflous methods for inserting the child. If I include it in my Repository, this makes testing very difficult (if not impossible).
Does anyone know how to resolve this?
I'm assuming this cannot be done automatically, therefore we need a query to insert the parent, retrieve the ID for the parent and assign it to the child's foreign key, and then insert the child.
The first assumption is correct, that is that you have to supply the id of the parent (otherwise how is to know the parent).
However the second assumption that you have to query the parent is not always the case and not so in the scenario you describe. If when inserting a parent then the id is returned if using the convenience #Insert as a Long or an array of Longs (if inserting multiple Parents).
For example, say you have :-
#Entity
data class Parent(
#PrimaryKey
var id: Long? = null,
var other: String
)
and
#Entity
data class Child(
#PrimaryKey
var id: Long? = null,
var parentId: Long,
var otherdata: String
)
and an #Dao annotated class with :-
#Insert
fun insert(parent: Parent): Long
#Insert
fun insert(child: Child): Long
Then you can use the following, without having to query the Parent:-
var lastParent = dao.insert(Parent(other = "Parent1 other data"))
dao.insert(Child(parentId = lastParent, otherdata = "Child1 other data"))
dao.insert(Child(parentId = lastParent, otherdata = "Child2 other data"))
// Insert a Child with it's Parent together
dao.insert(Child(
parentId = dao.insert(Parent(other = "Parent2 other data")),
otherdata = "Child3 other data"
))
note even if you define the id's as Int, a Long is returned when inserting.
It is incorrect to use Int for an id as SQLite stores the id as a 64bit signed integer which is to large for an Int.
However, issues would not occur until the id reached a value that is too large for an Int (32bit signed) i.e. greater than 2,147,483,647.

Prisma: Create or Connect Records in Explicit Many-to-Many Relations

In my Prisma Schema, I'm finding it difficult to undertand how to to create records in case of explicit many-to-many relations.
I have the following schema. Basically it represents Lists of Books. Users can Create Lists of Books.
A user can create a New list and then add books to this list along with their own notes. The Book Model is pure and contains standard book information.
The extra model is required because the user who is adding the book to the list can add his own notes about the book.
model List {
id Int #default(autoincrement()) #id
title String
slug String?
content String?
published Boolean #default(false)
author User? #relation(fields: [authorId], references: [id])
authorId Int?
books BooksInLists[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model BooksInLists {
list List #relation(fields: [listId], references: [id])
listId Int // relation scalar field (used in the `#relation` attribute above)
book Book #relation(fields: [bookId], references: [id])
bookId Int // relation scalar field (used in the `#relation` attribute above)
##id([listId, bookId])
adder User? #relation(fields: [adderId], references: [id])
adderId Int?
notes String?
}
model Book {
id Int #id #default(autoincrement())
name String
lists BooksInLists[]
curator User? #relation(fields: [curatorId], references: [id])
curatorId Int?
bookDescription String?
}
model User {
id Int #default(autoincrement()) #id
name String?
email String? #unique
lists List[]
books Book[]
booksinlists BooksInLists[]
##map(name: "users")
}
The queries that I want to be able to do.
While updating a list, I should be able to add a new book to the list. This should create the new book, and also allow me to add a new record in BooksInLists model along with the 'notes' field.
While updating a list, I should be able to add/connect an existing book to the list. This will allow me to add a new record in BooksInLists model along with the 'notes' field.
It will be something like that:
prisma.booksInLists.create({
data: {
list: {
connect: {
id: 99
},
},
book: {
create: {
name: 'Young Lions'
}
}
}
})
However I see flaws in database schema. Model BooksInLists connects Books and List, so you don't need adder relation. In turn in Book model you shouldn't add curator relation because it's many to many relation. You have to use junction table usersBooks that connects User and Book tables.

NHibernate SaveOrUpdate without primary key

The Situation
I've got a database table that is mapped via NHibernate (3.3.3-SP1). The application is running on .NET4.0 and the mapping is done via FluentNHibernate (1.4.0).
CREATE TABLE Movies
(id INT PRIMARY KEY,
yearPublished DATETIME NOT NULL,
name NVARCHAR(500) NOT NULL,
description NTEXT NOT NULL)
The data would be something like this:
id | yearPublished | name | description
---+---------------+------------------------+--------------------------------------------
1 | 1968 | 2001: A Space Oddyssey | An epic drama of adventure and exploration
The Problem
I'm creating new entities of this table and want to avoid adding more than one entity for the same real world thing. I know that there is Session.SaveOrUpdate and that there is also a way to make it work with composite and natural ids but that's not really what I want since my entities actually have a primary key and I really only need the composite key for making sure that no duplicates are in the DB.
var movie = new Movies
{
yearPublished = 1968,
name = "2001: A Space Oddyssey",
description = "An awesome journey to Jupiter"
};
// Behavior right now:
// Adds a new movie besides the fact that
// the movie is already in the database
// but now has two entries
session.SaveOrUpdate(movie);
Assert.IsTrue(movie.id == 2 && movie.description == "An awesome journey to Jupiter");
// What I really want is to be able to define what
// makes an object unique other than the primary key;
// in this scenario it should look for a combination
// of "yearPublished" and "name"
session.MyAwesomeSaveOrUpdate(movie);
Assert.IsTrue(movie.id == 1 && movie.description == "An epic drama of adventure and exploration");
Is this functionality in place in NHibernate (e.g. through a custom mapping) or do I have fetch the candidates from the DB and do it by hand?
Thanks!
I solve this by adding a unique constraint on the natural key fields in the database and using an exception converter to convert the SQL Server exception into one my application can handle.
public class SqlServerExceptionConverter : ISQLExceptionConverter
{
public Exception Convert(AdoExceptionContextInfo adoExceptionContextInfo)
{
var sqlException = adoExceptionContextInfo.SqlException as SqlException;
if (sqlException != null)
{
// 2601 is unique key, 2627 is unique index; same thing:
// http://blog.sqlauthority.com/2007/04/26/sql-server-difference-between-unique-index-vs-unique-constraint/
if (sqlException.Number == 2601 || sqlException.Number == 2627)
{
// my custom exception
return new UniqueKeyException(sqlException.Message, sqlException);
}
}
return adoExceptionContextInfo.SqlException;
}
}
Another approach I can think of is to query the database for a matching record before the insert but that's not foolproof because a record could be inserted between the select and your insert.

What should I make the type of a "marital status" field?

I have a field in my table "marital status" , the user has to choose (radiobutton) if he's (married, divorced, single, voeuf)
What should I make the type of this field?
Is there a boolean type?
marital status doesn't sound like a boolean anyway. It sounds like an enumeration. A boolean would be married (Y/N), although I think in this day and age you might want to be able to store multiple kinds of relationships in there, and you specified yourself that you need to store 'devorced' as well, so a boolean is out of the question.
So I'd recommend making a table named MaritalStatus, having an ID and a description. Store the various states in there, and make a foreign key to MaritalStatusID in your table.
Make it an INT field , Create another table in your database something like
CREATE TABLE dbo.MaritalStatus
(
M_ID INT PRIMARY KEY NOT NULL,
M_Status NVARCHAR(20)
)
GO
INSERT INTO dbo.MaritalStatus
VALUES
(1, 'Single'),(2,'Married'),(3,'Divorced'),
(4,'Widowed'),(5,'Other'),(6,'Prefer Not to say').... bla bla
Now in your Table in "Marital Status" field refer to a user Marital Status using INT values from dbo.MaritalStatus table's "M_ID".
Boolean or in SQL bit datatype is best when you have a situation where something can be TRUE or NOT TRUE, for someone's Marital Status there can be more than two possible values therefore you should create a separate table for all the possible Marital Status and use Foreign key constraint.
The boolean equivalent for T-SQL is bit.
Though, it seems like you want more than a yes/no answer. In this case use an int and then convert the int to an enum.
Edit: Dukeling removed the C# tag in an edit, so I am not sure how relevant this part is anymore /Edit
The enum:
enum MaritalStatus
{
Single,
Married,
Divorced,
...
}
The int from DB:
int maritalStatusFromDB = //value from DB
Convert int to enum:
MaritalStatus maritalStatus = (MaritalStatus)maritalStatusFromDB;
Be aware that your database may contain int values that are not defined in your enum, such as 10. You can check whether maritalStatusFromDB is a valid MaritalStatus as follows:
bool isValid = Enum.IsDefined(typeof(MaritalStatus), maritalStatusFromDB);
if( isValid == false )
{
//handle appropriately
}

JPA-how to Add record to master table and child table?

I am having two tables One is a Master table called TRANSACTION and second is record of the transaction this table is called TRANSACTION_RECORD.
TRANSACTION
CREATE TABLE `e3_transaction` (
`transactionid` bigint(20),
`transactiontype` varchar(10),
`transactionstatus` varchar(10),
PRIMARY KEY (`transactionid`)
);
TRANSACTION_RECORD
CREATE TABLE `e3_as2805msg4` (
`transectionid` bigint(20),
`messageType` int(4),
`cardAcceptorName` varchar(40),
`adNational` varchar(1000),
`adPrivate` varchar(1000),
KEY `transectionidFK` (`transectionid`),
CONSTRAINT `transectionidFK` FOREIGN KEY (`transectionid`) REFERENCES `e3_transaction` (`transactionid`)
);
It will have one to one mapping between Transaction and transaction record. It means one transaction can have only one record. I have kept this table separately for some reasons. So my class will look like this:
#Entity
#Table(name = "e3_transaction")
public class Transaction {
#Id
#GeneratedValue(generator = "assigned-by-code")
#GenericGenerator(name = "assigned-by-code", strategy = "assigned")
#Column(name = "transactionid", unique = true, nullable = false)
private Long transactionid;
#Column(name = "transactiontype", nullable = false, length = 10)
private String transactiontype;
#Column(name = "transactionstatus", nullable = false, length = 10)
private String transactionstatus;
#oneToOne
private TransactionRecord record;
}
I want to persist both objects at a same time. when I persist a TRANSACTION, TRANSACTION_RECORD should be persist in it's table. Is there any way to do this ?
You can change the table structure if you want. Only thing i need it TWO tables.
Works with
#OneToOne(cascade=CascadeType.ALL)
#MapsId
private TransactionRecord record;
TransactionRecord must have an #Id of the same type as Transaction with no value generation.
Tried with Hibernate as JPA 2.0-provider.
There are a few options to map this, but it looks like you are handling it with two separate entities. As both entities share the same primary key value, your reference mapping will need to change based on which entity you wish to have controlling the pk value generation - as it stands, the e3_transaction.transactionid field is being set by two separate mappings; the transactionid long and the TransactionRecord record reference.
If you wish to use the #MapsId as is suggested in the other answer, you will need to move your #GeneratedValue code to the TransactionRecord entity, as the JPA provider will use the value in the referenced TransactionRecord to set the transactionid attribute and the database field. This is a simple elegant solution, but you can also remove the Long transactionid attribute from Transaction and just mark the record reference with #Id (instead of #MapsId). The long transactionId value within TransactionRecord would still be used as Transaction's id for EntityManager getReference and find calls.
A different option that allows keeping the #GeneratedValue on the transactionid within Transaction is to define the #JoinColumn annotation on the record reference and specify that the field is insertable=false, updatable=false. You then need to modify the TransactionRecord so that it has a back relationship to the Transaction so that it can pull the transectionid value from the Transaction instance to use as its id. This can be accomplished by simply marking the relationship with #ID though.