GORM: Create() with different Input and Output Structs - go-gorm

Is it possible in GORM to give Create() a Struct A as input and store the result in Struct B?
I haven't found anything suitable in the documentation or on the internet.
The background is as follows:
I let Postgres create the fields id, createdAt and updatedAt - therefore they should not be part of the input Struct A (quasi the DTO/DAO).
However, in the result of Create() these fields are then present - so they should be parsed into Struct B, which contains all the fields of the table.
Problem if id, createdAt and updatedAt are not explicitly set:
Go initialises fields of structs that are not explicitly defined with default values, depending on the data type (0, nil etc.).
For the id of type UUIDv4, the value is then 00000000-0000-0000-0000000000.
Apparently this passed value then overwrites Postgres' gen_random_uuid() value for the id column.
Once this works fine, then a duplicyte key error occurs.
createdAt and updatedAt, on the other hand, seem to be generated correctly by Postgres, although I have found Go's default values there as well.
Thank you!
PS: Of course, I could simply create a UUID and give it to the database, but I am basically interested in whether or not my intention can be realised with GORM.

You don't need two separate structs, you can use your model to insert/fetch. One way of doing it is to embed gorm.Model, which will embed
// gorm.Model definition
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
Embedding into User struct.
type User struct {
gorm.Model
Name string
Age int
}
// equals
type User struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
Age int
}
Now creating records
user := User{Name: "randomname", Age: 18}
result := db.Create(&user) // pass pointer of data to Create
user.ID // returns inserted data's primary key
user.CreatedAt // returns inserted data's created at key
user.UpdatedAt // returns inserted data's updated at key
Embedding gorm.Model is optional, you can embed it or add the fields to the struct directly or you can create your own struct to embed.
Even if at the time of insert those fields [updatedAt, createdAt] are empty or not part of the input struct, the result will return whatever that is there in database. If the createdAt or updatedAt are created by postgres and nonEmpty it will be returned.
update :- We can use default values
type HasUuidPkey struct {
ID uuid.UUID `gorm:"primaryKey;default:gen_random_uuid()"`
Name string
CreatedAt time.Time
}
Playground example

Related

How to set fields non-nullable for JOOQ POJO

I have this DDL
CREATE TABLE user
(
id bigint NOT NULL PRIMARY KEY,
name string NOT NULL
);
org.jooq.codegen.KotlinGenerator generates such a class:
data class Users(
var id: Long? = null,
var name: String? = null,
): Serializable
I expect non-nullable fields to be non-nullable, like:
data class Users(
var id: Long,
var name: String,
): Serializable
I only use these settings:
generate.apply {
isRecords = true
isDaos = true
isPojosAsKotlinDataClasses = true
isSpringAnnotations = true
isPojos = true
}
Jooq ver. 3.15.10
How do I configure the generator so that the fields are non-nullable?
As of jOOQ 3.17, there are pending feature requests to add some additional convenience at the price of correctness to generated classes:
#10212 Add option to handle nullability in KotlinGenerator
#12934 Null-safe views of columns known to be non-null
The main reason why this hasn't been implemented yet is the many ways such non-nullability promises can break in the event of using operators like LEFT JOIN, UNION and many others, in case of which an expression that appears to be non-null will produce null values nonetheless.
While #10212 is very hard to get right, #12934 might be a compromise to be implemented soon, given that it affects only generated data class types, which can be used on a 1:1 basis by users who understand the tradeoffs.

Sort records with an associated field

I want to order records with associated records in GORM V2.
I have two structs:
type Client struct {
gorm.Model
UUID uuid.UUID `gorm:"type:uuid"`
ClientProfile ClientProfile
}
type ClientProfile struct {
gorm.Model
ClientID uint
FirstName string
LastName string
}
Now, I can load all the records with:
db.
Preload(clause.Associations).
Find(&clients).
How can I now order these records with a field from the ClientProfile struct?
I tried without success:
db.
Preload(clause.Associations).
Order("client_profiles.last_name DESC").
Find(&clients)
This throws the following error:
ERROR: missing FROM-clause entry for table "client_profiles" (SQLSTATE 42P01)
Find with Preload queries the clients table first, and then queries the client_profiles table with the IDs from the first, thus it can't sort the first query by data from the second one.
Since this is a HasOne relationship you can use Joins Preloading which actually does use a single query for both entities. Then you will be able to sort by the related entity's field:
err := db.
Model(&Client{}).
Joins("ClientProfile").
Order("client_profiles.last_name DESC").
Find(&clients).
Error
// handle error

Gorm Join table query

I currently have a Club and Tag table and I wanted to obtain all of the clubs that have at least 1 of those given tags.
type Club struct {
ID uint
Sets []Tag `gorm:"many2many:club_tag;foreignKey:id;References:name"
}
type Tag struct {
ID uint
Name string
}
I made this query which ends up returning the clubs that have that particular tag most of the time however, sometimes I encounter these issues.
var clubs []Club
var tags []string
db.Joins("JOIN club_tag ON club_tag.club_id=club.id).
Joins("JOIN tag ON club_tag.tag_name=tag.name").
Where("tag.name IN ?", tags).
Find(&clubs)
> ERROR: column club_tag.deleted_at does not exist (SQLSTATE 42703)
> Error: missing FROM-clause entry for table "club_tag" (SQLSTATE 42P01)
Am I missing something in this query such that it won't return an error?
I'm currently using GORM v2 and tried to follow this example
Any help is appreciated! Thanks :)
based on your errors :
ERROR: column club_tag.deleted_at does not exist (SQLSTATE 42703)
Error: missing FROM-clause entry for table "club_tag" (SQLSTATE 42P01)
Your first error says you club_tag hasn't deleted_at, I think your struct has gorm.Model data. from documentation of gorm v2:
GORM defined a gorm.Model struct, which includes fields ID, CreatedAt, UpdatedAt, DeletedAt
when you create query for struct which has gorm.Model. gorm in default created deleated_at = null on query. so I think you should remove gorm.Model and create what fields you want.
Your second error says your query hasn't any table on join. so I think you must check your table names.

enum as string and number in the database

i have a table
table Foo
(
id,
FooType,
FooTypeName,
)
FooType is an enum with fixed values hence i want to map it to
public virtual FooType FooType { get; set; }
How can i serialize the property in both columns but use only one to read and query?
Which mapping (xml, Mapping by Code, FluentMapping) doesn't matter
You could create FooTypeName as a string property projecting the value of FooType, and map it as readonly.
Alternatively, if you don't need FooTypeName at all, just ignore it in your model and keep it updated with a trigger.
Or, if you feel like coding a lot, create a user type that maps FooType to the two columns.

NHibernate Mapping from multiple tables

Consider the following table structure...
Appointment
-----------
ID integer
Description nvarchar
StatusID smallint
Status
------
ID smallint
DisplayText nvarchar
Now, for good or for evil, we want this situation to map to a class that looks like this
class Appointment
{
public int ID {g;s;}
public string Description { g;s; }
public string Status { g; private s; }
}
I.e. we want to pull the normalised status display text straight into the entity.
The obvious answer is to create a Status entity and make the appointment class have a reference to that and map it in the normal way.
Don't create an entity class. Use an enum and EnumStringType as shown here. This is exactly what you want I think.