Simplest way to detect if any model value has changed - go

I'm using gorm as a Golang ORM. I need to detect if any model field has changed to trigger a update on a thirdy-party API service.
I have tried to test every field with an If statement, but it gets ugly when the model has many fields.
var person Person
db.Where("id = ?", id).First(&person)
if person.Name != body.Person.Name || person.Age != body.Person.Age {
// Trigger API update
}
db.Save(&person)
Is there a easy way to achieve this?

I don't know if this is the simplest way, and it is probably not idiomatic, but you can accomplish this with reflection. The following function uses the reflect Package to compare two Person structs to see if their values for each field are the same, skipping the Model struct (whose internals vary independently of the data element the Person represents).
func (this Person) Equals(that Person) bool {
vThis := reflect.ValueOf(this)
vThat := reflect.ValueOf(that)
for i := 0; i < vThis.NumField(); i++ {
if vThis.Field(i) != vThis.FieldByName("Model") {
if vThis.Field(i).Interface() != vThat.Field(i).Interface() {
return false
}
}
}
return true
}
You could use this in your code snippet then as follows:
if !person.Equals(body.Person) {
// Trigger API update
}
Note that I'm quite new to go, so I may be leading you astray here in terms of "proper" code. But this does work.

GORM does provide such a feature via the 'Changed' method which could be used in Before Update Hooks. It returns if the field was changed or not.
https://gorm.io/docs/update.html#Check-Field-has-changed

Related

How to use http patch request in spring boot?

I am trying to use PATCH request.
below is the code which i am using. and it is like ladder of if statements
#PatchMapping("/updateInvoiceByEmail/{email}")
public Mono<ResponseEntity<Invoice>> updateInvoiceByEmail(
#PathVariable String email,
#RequestBody Invoice invoice) {
return invoiceRepository
.findByEmail(vendorEmail)
.flatMap(existing -> {
if (invoice.getInvoiceStatus() != null) {
existing.setInvoiceStatus(invoice.getInvoiceStatus());
}
if (invoice.getCanRaise() != null) {
existing.setCanRaise(invoice.getCanRaise());
}
if (invoice.getAttachmentId() != null) {
existing.setAttachmentId(invoice.getAttachmentId());
}
if (invoice.getInvoiceId() != null) {
existing.setInvoiceId(invoice.getInvoiceId());
}
... and so on.
return invoiceRepository.save(existing);
})
.map(updatedInvoice -> new ResponseEntity<>(updatedInvoice, HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
I am using
Spring WebFlux and mongodb
How can i make shorter and cleaner.
Thanks
You can use reflection if you want to reduce number of lines although reflection is always last thing that you should do. What I would do is to move this if - get - set logic in separate component(in separate class or method). Aside from that, I would just like to mention that simplicity is not the only problem here. Assume that null is valid value sent by client, you will need some mechanism how to detect if value is not sent or you explicitly wanted to set null value. Some example of moving this code to separate component would look something like this:
class InvoiceAssembler {
public static assemble(Invoice existing, Invoice newInvoice) {
if(newInvoice.getInvoiceId() != null) {
existing.setInvoiceId(newInvoice.getInvoiceId());
}
...
}
}
Only way I see you could improve is if you use a UpdateDTO for the fields that are most likely to be updated and therefor reduce the amount of fields you have to check.
Good description of how you use UpdateDTO here
Otherwise you are left with reflection but I don't know if that would make your code more readable or more confusing instead.

Handling optional boolean values

I'm failing to understand how I can work around the problem I'm experiencing in my own application.
Imagine this example of a struct that models an incoming request and a function that puts the fields from that request into a database.
type NewBooleanRequest struct {
RequiredString string `json:"requiredString"`
OptionalBoolean bool `json:"maybeBoolean"`
}
func LogBooleanRequest(req NewBooleanRequest, db *sql.DB) {
db.Exec("INSERT INTO log (booleanValue, stringValue) VALUES ($1, $2)", req.OptionalBoolean, req.RequiredString)
}
Now this obviously works fine if I know I will be given a value for all fields of my request model, but that's not a common requirement in reality. How do people generally model "optional" semantics for bool values given that bool has a zero value that is valid in essentially all contexts?
This question isn't specific to booleans, it's common for all NULLable types. The simplest solution is to use a pointer (*bool in your example). There are also Nullable values for common types provided by the sql package. sql.NullBool would be the one you want in this case.
Beside the accepted answer, I found this interesting library https://github.com/guregu/null that can deal with nullable value pretty well. It's very useful to declare API request params without messing with sql library on the server routing (which is not so relevant).
For your case, you can write your request like:
type NewBooleanRequest struct {
RequiredString string `json:"requiredString"`
OptionalBoolean null.Bool `json:"maybeBoolean"`
}
request := NewBooleanRequest {
RequiredString: "Your string",
OptionalBoolean: null.BoolFrom(false)
}
if request.OptionalBoolean.Valid {
fmt.Println(request.OptionalBoolean.Bool) // Print "false"
} else {
fmt.Println("request.OptionalBoolean is NULL")
}

GORM Golang how to optimize this code

I use GORM in my project and I want to create something like DB admin page.
To load records I send GET with params:
category: "name", // database table name
On server I have the next code:
func LoadItems(db *gorm.DB, category string) interface{} {
var items interface{}
loadItems := func(i interface{}) {
err := db.Find(i).Error
if err != nil {
panic(err)
}
items = i
}
switch category {
case "groups":
var records []*models.Groups
loadItems(&records)
case "departments":
var records []*models.Departments
loadItems(&records)
case .....
........
}
return items
}
Is it possible to replace switch because I have 10 tables and after record editing I send new data to server, where I re forced to use switch in other function to save it.
I'm not familiar with gorm, but:
Maybe store "department" (as key) and a variable of the corresponding model type in a map, and then referencing via key to the model. If not already, the models then have to implement a common interface in order to be able to store them in one map.
Though, if that would be a better solution I'm not sure. Maybe a bit easier to maintain, because new model types only have to be added to the map, and you don't have to adjust switches on multiple places in your code.
Another obvious way would be, outsourcing the switch to a function, returning a variable of a common interface type and using that on different places in your code. That would definitely not be faster, but easier to maintain.

When avoiding global vars (/state), i find myself linking objects backwards to its parent. Am I doing this right? if not explain why? and how-else?

Note: Im just picking the current struct/example to explain the problem.
type MsgBoxFactory struct{
db *dbSql //contains conn-pool and other DB related settings/flags
}
func (f *MsgBoxFactory) NewMsgBox(userId string) {
return MsgBox{userId, f.db} //f.db link is inevitable
}
type MsgBox struct {
ownerId string
db *dbSql
}
func (m *MsgBox) NewMessage(content string) *Message {
return Message{content, *m.dbSql} //m.dbSql link is inevitable
}
type Message struct {
content string
//other fields such as recipents, isRead, created time etc.
db *dbSql
}
func (m *Message) Send(to string) {
message.to = to //just imagine this saves the message to database.
m.db.Save(message)
}
I tend to call this "backward-referencing"[i don't know actual name]... Is this the only way? Previously i was "backward-referencing" entire parent objects. Now i find myself "backward-referencing" objects such as config/dbconn etc...
Is this a good way? what is better?
Oh i have also tried closure to get rid of it atleast from view.
type Message Struct{
content string
Send func(string) error // the problem is `json:"-"` needs to be added. Else the objects are not json compatible
}
func (m *MsgBox) NewMsg(content string) *Message {
msg := &Message{ content }
msg.Send = func(to string) error {
return m.db.Save(msg)
}
}
Basically the code looks almost equally cluttered with unnecessary complexity/code
EDIT: The question is not specific to go. Just posted it because i use go. Any tag suggestion is appreciated to open the question for wider community.
I usually implement a model helper relationship.
Where MsgBox is your model which has all the data specific elements (No DB related elements).
The MsgBoxHelper does all your database related work.
(i.e.
err := MsgBoxHelper.Save(MsgBox)
msgBox, err := MsgBoxHelper.Load(Key)
)
Edit:
The advantage with this method is it decouples your Model from the datastore, which should inturn make it easier should you wish to change your underlying technology (Which doesn't often happen). In practice it's more useful should you start doing things like caching.
If generically you are referencing other structures within your model i.e.
type MsgBox struct {
Colour *MsgBoxColour
...
}
type MsgBoxColor struct {
ID int
...
}
then when you load the Model in your MsgBoxHelper you call the MsgBoxColourHelper with the ID you stored in the MsgBoxColour table, this then returns your MsgBoxColour that you then associate with the returning MsgBox.

How can I create temporary records of Linq-To-Sql types without causing duplicate key problems?

I have code that generates records based on my DataGridView. These records are temporary because some of them already exist in the database.
Crop_Variety v = new Crop_Variety();
v.Type_ID = currentCropType.Type_ID;
v.Variety_ID = r.Cells[0].Value.ToString();
v.Description = r.Cells[1].Value.ToString();
v.Crop = currentCrop;
v.Crop_ID = currentCrop.Crop_ID;
Unfortunately in this little bit of code, because I say that v.Crop = currentCrop,
now currentCrop.Crop_Varieties includes this temporary record. And when I go to insert the records of this grid that are new, they have a reference to the same Crop record, and therefore these temporary records that do already exist in the database show up twice causing duplicate key errors when I submit.
I have a whole system for detecting what records need to be added and what need to be deleted based on what the user has done, but its getting gummed up by this relentless tracking of references.
Is there a way I can stop Linq-To-Sql from automatically adding these temporary records to its table collections?
I would suggest revisiting the code that populates DataGridView (grid) with records.
And then revisit the code that operates on items from a GridView, keeping in mind that you can grab bound item from a grid row using the following code:
public object GridSelectedItem
{
get
{
try
{
if (_grid == null || _grid.SelectedCells.Count < 1) return null;
DataGridViewCell cell = _grid.SelectedCells[0];
DataGridViewRow row = _grid.Rows[cell.RowIndex];
if (row.DataBoundItem == null) return null;
return row.DataBoundItem;
}
catch { }
return null;
}
}
It is also hard to understand the nature of Crop_Variety code that you have posted. As the Crop_Variety seems to be a subclass of Crop. This leads to problems when the Crop is not yet bound to database and potentially lead to problems when you're adding Crop_Variety to the context.
For this type of Form application I normally have List _dataList inside form class, then the main grid is bound to that list, through ObjectBindingList or another way. That way _dataList holds all data that needs to be persisted when needed (user clicked save).
When you assign an entity object reference you are creating a link between the two objects. Here you are doing that:
v.Crop = currentCrop;
There is only one way to avoid this: Modify the generated code or generate/write your own. I would never do this.
I think you will be better off by writing a custom DTO class instead of reusing the generated entities. I have done both approaches and I like the latter one far better.
Edit: Here is some sample generated code:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="RssFeed_RssFeedItem", Storage="_RssFeed", ThisKey="RssFeedID", OtherKey="ID", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
public RssFeed RssFeed
{
get
{
return this._RssFeed.Entity;
}
set
{
RssFeed previousValue = this._RssFeed.Entity;
if (((previousValue != value)
|| (this._RssFeed.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._RssFeed.Entity = null;
previousValue.RssFeedItems.Remove(this);
}
this._RssFeed.Entity = value;
if ((value != null))
{
value.RssFeedItems.Add(this);
this._RssFeedID = value.ID;
}
else
{
this._RssFeedID = default(int);
}
this.SendPropertyChanged("RssFeed");
}
}
}
As you can see the generated code is establishing the link by saying "value.RssFeedItems.Add(this);".
In case you have many entities for wich you would need many DTOs you could code-generate the DTO classes by using reflection.

Resources