Does Prisma support composite keys with one of the attribute being NULL for PostgreSQL? - prisma

In PostgreSQL you can make a table with 2 columns as the composite key of that table, with one of them being NULL-able. I was just wondering how I can achieve this with Prisma.
In the current version of Prisma that I have (3.14.0), Prisma does allow composite key using ##id([column1,column2]), but only if those two columns are mandatory.

No, Prisma doesn't support composite keys with optional columns. ID Definitions are required.
Example:
This would work
model Post {
title String
content String
##id([title, content])
}
But this wouldn't as column content is defined as optional
model Post {
title String
content String?
##id([title, content])
}
You can create a Feature Request here to support allowing nullable columns in composite id.

Related

Postgres NestJS Prisma migration - Database error code: 23502 column of relation contains null values

I updated my Prisma ORM database model by adding two more fields 'ahash' and 'sAddress' to the 'invitation' table.
The table already contains 6 rows of data.
When I try to migrate the new changes to the Postgresql database, I get the error Database error code: 23502. ERROR: column "aHash" of relation "Invitation" contains null values.
How do I cater for the null values and migrate the model updates smoothly onto the database?
Give me a Step by step account please. I'm new to Prisma migration.
Thanks in advance!
The Prisma invitation model looks like below.
model Invitation {
id String #id #db.Uuid
workId String #db.Uuid
work Work #relation(fields: [workId], references: [id])
status RequestStatus
coId String #db.Uuid
oSignature String
note String
aHash String
sAddress String
createdAt DateTime
respondedAt DateTime
}
The problem here is that ahash and sAddress are both mandatory fields. However, they do not exist for the 6 existing rows/records in your database.
If you want to add new columns to an existing database without causing data loss you need to make sure the new columns/field are optional. This can be done in Prisma by marking the type of the field with a ?.
If you need the field to be mandatory, you could do it in three steps:
Create the new ahash and sAddress fields as optional first in your prisma schema and run a migration.
Run a script to update all existing records, so that they have a value for the ahash and sAddress fields.
Mark both fields as mandatory in your Prisma schema and run a migration.
In step 3, you will no longer get the error because none of the records contain a null value for the ahash and sAddress fields.

JPA how ensure uniqueness over 2 fields, string and boolean

I want to create an entity containing 2 fields that need to be unique in together. One of the fields is a Boolean:
#Entity
public class SoldToCountry {
private String countryId;
private Boolean isInt;
}
For a given String there should never exist more than 2 entries one with isInt:true and the other isInt:false.
I read the doc about #Id but it seems that Boolean is not supported. For me it would also be ok to have a unique constraint spanned over both fields and using a generated id.
What is the best way to get this constraint via JPA?
If your table has really two fields only, and you want they are unique, then they should be the composite PK of the table. Take a look at How to create and handle composite primary key in JPA
If, instead, you have another PK, consider Sebastian's comment.

JPA: generate non pk unique and random alphanumeric value

I want to uniquely identity an entity without using the primary key. So I thought about generating an unique and random value. Moreover, value must be easy to read / manually copy and is expected to be 6 or 7 characters long.
Design
My entity A:
public class A{
// ...
#Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
private String value;
// ...
public String getValue(){
return value;
}
protected void setValue(String value){
this.value = value;
}
}
represented in the database by the table
CREATE TABLE IF NOT EXISTS schema.mytable{
-- ...
value TEXT NOT NULL DEFAULT generate_unique_value_for_mytable(),
-- ...
CONSTRAINT "un_value" UNIQUE (value),
-- ...
}
I thought letting the database handling this and then fetch the value...
Problem
With the current design, value is correctly generated in the database but when JPA fetches A entities, value field is empty.
I cannot remove insertable=false otherwise, it will hit against the NOT NULL constraint
If I remove insertable=false and I put some dummy data, the data overrides the value generated by generate_unique_value_for_mytable()
If I remove everything in the Column annotation, I can save the A entity but value is still empty
Ugly solution
I couldn't find a proof but it looks like having the database generating a value is a bad idea. I do have the same problem for a non-primary key field which is generated by a sequence: I cannot fetch the value from the database.
So my ugly solution is to decorate the create() method of the EJB responsible for A entities:
public class Aejb{
public void create(A entity){
// method kind of ensures randomness
String value = MyUtil.generateRandomValue();
A isThereAnyoneHere = findByValue(value);
while(isThereAnyoneHere != null){
String value = MyUtil.generateRandomValue();
isThereAnyoneHere = findByValue(value);
}
// unicity is ensured
entity.setValue(value);
em.persist(entity);
}
}
Questions
Can I fetch a non-primary key value generated by the database from a JPA entity? Value can be generated by a function or a sequence.
Is there a more elegant solution than my ugly workaround to provide an unique and random value?
Yes.You haven't mentioned your database, but it is possible for
Oracle to return the value inserted via triggers, and have
Eclipselink obtain this value in your model - see
https://www.eclipse.org/eclipselink/documentation/2.5/jpa/extensions/a_returninsert.htm
Set the value using a #PrePersist method that will get executed
before the entity is inserted, but if you are relying on one or more database queries, you will run into performance issues, as inserting a new A will be expensive. You might instead just insert the random value and deal with the occasional conflict, and pick some random that has less chance of overlaps, like a UUID.
If I understand correctly, #Generated annotation should do the trick. This annotation sets the value from database DEFAULT field value.
Example:
#Generated(GenerationTime.INSERT)
#Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
private String value;
However there is a drawback: if you decide to set value of your field in Java, it would be overwritten by Hibernate using the result from DEFAULT in your database.
Self-answer to mark question as closed
Final solution
We finally went for a combination of
Stored procedures: the database will generate the value. The procedure also ensures that the value is unique across the table
Named queries: to fetch the generated value by the procedure. I did not use NamedStoredProcedures because we are using PostgreSQL and PostgreSQL JDBC driver did not support name parameters which raised some problems.
With this configuration, the EJB is sure to have at most one database call to fetch the requested value.
Response to other answers
Here is a summary of the other answers feedback for self-reference and next readers:
Oracle trigger: we're using PostgreSQL :(
UUID: We had the constraint of having our unique and random code human-readable. An end-user is assumed to be able to manually rewrite it. Consequently, we could not have a long String such as an UUID.
PrePersist: Other business actions take place after the code generation in the same transaction which means that those actions need to be redone in case of collision. I'm not very confident about managing JPA exception (transaction scope and so on) so I preferred not to play with it.
#Generated: This is a Hibernate specific feature. We're using EclipseLink
Database Trigger: If code were purely generated at database level, I encountered the same problems of not fetching the value: the value is properly generated as database level but the entity will have the value as null

Web API Entity Framework error - Item with idenity already exists in the metadata collection

I am experimenting with a Web API 2 project in Visual Studio 2012. I used the code first from existing DB option with EF6 to select one table and one view. I then tried to create a controller for the simple table using the profile for Web API 2 OData. The scaffolding of the controller fails telling me that "the item with identity 'Client Last Reveiwed On' already exists in the metadata collection". The problem is not only am I sure that field is unique for this project but that field is part of the view and not the table. Below is the model generated for the simple table (t_Client) that I was trying to create the controller for. As you can see the offending column is not part of the class. I will add below the definition for the column that VS/EF doesn't like which is in the class for the view.
Any ideas why this won't work?
Partial Public Class t_Client
<Key>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Property ClientID As Integer
<Required>
<StringLength(255)>
Public Property ClientName As String
Public Property isActive As Boolean
End Class
Here is the column that is defined in a separate view.
<Column("Client Last Reviewed On", TypeName:="date")>
Public Property Client_Last_Reviewed_On As Date?
I am not sure which of these steps fixed the issue but here are some notes on the topic.
Removing references to the model based on the SQL view eliminated errors.
I went into SQL and updated the view to contain a row number column.
Even with the row number column, EF tagged multiple columns as the key.
I manually edited the model to make the row number column the key.
I also had to update a cast the data type of a few columns in the SQL view to match reality, mainly bigint that was really just integer.
My guess is the fix was the well defined key.

JPA Relation or Secondary Table

I am reengineering one of my project with JPA which was initially on iBatis.
public class Entity{
//ids and other stuff
String locale;
String text;
}
I was storing locale and text into separate table, which can be achieved via secondary table in JPA.
I am not able to create secondary table with its own ids besides Join
id?
how can I achieve it? If possible then it raises the following
question:
how would I retrieve it back if I create an entity object with locale set to user settings
See,
http://en.wikibooks.org/wiki/Java_Persistence/Tables#Multiple_tables
Otherwise include your exact schema and model and issue.