community.
I am just start to use droidparts.
As I got it is cool library. But it has poor documentation, and no comments in code itself.
I am interested in getting elegant approach to use it.
Well suppose, i've created tables items, users, items2users as many-to-many concept.
Tables items and users are trivial. Table items2users is
#Table(name="items2users")
public class ItemsToUsers extends Entity {
#Column
public Item item;
#Column
public User user;
}
So if I need to get answer if my User has Item I do something like that in UserManager class:
public boolean hasItem(Item item) {
Select<ItemsToUsers> select = select().columns(ID).where({HERE_I_NEED_COLUMN_NAME}, Is.EQUAL, user.id);
}
Droidparts made table with fields 'item_id' and 'user_id'. As I understand HERE_I_NEED_FIELD_NAME must be 'user_id'. As I realize it is automatic naming model for this situation?
Well is there some elegant technique to achive this names or I should construct them manually getting 'user' and '_' and 'id'?
By default the column name will be field name + _id, e.g. item_id & user_id in your case. That can be altered through annotation, e.g. #Column(name="some_other_name").
Related
I'm working on a functionality, where in I have an entity called 'Employee' with a few columns like first name, last name, DOB, etc. and two columns:createdAt and updatedAt defined with #CreateDateColumn and #UpdateDateColumn respectively.
My requirement is that, whenever an update operation is performed on this entity, I should be logging the column name that was updated, along with the old and new value that it holds.
I'm performing the update on Employee table using queryRunner.manager.save()
To track the changes,
I'm making use of the entity subscribers 'afterUpdate' event.
Now on performing an update, for e.g. When I change the Name, my Employee entity is correctly updating the name as well as the 'updatedAt' field to current timestamp in DB
However,my 'afterUpdate' listener is only logging the 'name' column with its old and new values.
Its not logging the updatedAt column.
Can anyone please help me understand what's possibly going wrong here?
Entity definition:
...All columns (first name, etc.)
#UpdateDateColumn({name:'updated_at',type:'timestamp',nullable:true})
updatedAt:Date;
Subscriber code:
#EventSubscriber()
export class EmployeeSubscriber implements
EntitySubscriberInterface<EmployeeEntity> {
constructor()
{}
listenTo() {
return EmployeeEntity;
}
afterUpdate(event: UpdateEvent<EmployeeEnitity>){
const {updatedColumns, dataBaseEntity,entity}=event;
updatedColumns.forEach(({propertyName})=>{
//Get the updated column name and old and new values
//col_name = propertyName;
//oldVal= databaseEntity[propertyName as keyof EmployeeEntity] as string,
//newVal= entity?.[propertyName as keyof EmployeeEntity] as string,
})
}
}
I tried using different save operations (via repository, queryRunner, manager), but it didn't work.
Official docs say the event is triggered only on save operation.
If I manually pass the date to updatedAt field, then I am able to get the field within the event handler, but I don't think that's the right approach as manual intervention should not be needed for #UpdateDateColumn
Working on nest.js with TypeORM/Postgres.
I've got a calculated_properties column on an entity, like so:
parent.entity.ts
#Entity()
export class Parent {
...
#Column('jsonb')
calculated_properties: CalculatedProperties;
}
calculated_properties.entity.ts
export class CalculatedProperties {
SomeCalc: number,
Other Calc: number,
NestedCalcs: NestedCalc,
With nestedCalc being some other similar types to calculated_properties.entity.ts.
The problem is, when I try to run the app, I get the following error message:
Entity "CalculatedProperties" does not have a primary column. Primary column is required to have in all your entities. Use #PrimaryColumn decorator to add a primary column to your entity.
But I've nowhere said this child type is an entity, and I don't want it to have a PrimaryColumn. In fact, setting an #PrimaryColumn() on it still shows the error - am I missing something obvious here?
I've been kinda confused by the relationships as I'm used to save relationship by id, while docs and examples I found suggest to get the entire object and use that instead (Isn't this strange???)
I found this on github addressing this issue ( https://github.com/typeorm/typeorm/issues/447 ) , where they suggest to use an object with just the id property, but it's from 2017. Is that a good way to do it ? And is it still the only way to do it ? (I find it pretty lame tbh)
async create( #Body() product: Product) {
product.category = <any>{ id: product.category };
return { payload: await this.repository.persist(product) };
}
Another one suggested to name the column as categoryId and it would work as expected (with id instead of object) but WHY? What does the name have to do with that ??
#Entity()
class Product {
#Column({ type: "int", nullable: true })
categoryId: number;
#ManyToOne(type => Category)
#JoinColumn({ name: "categoryId" })
category: Category;
}
I'm just confused, help ^_^
Isn't this strange???
Depends how you think about it, but yeah, I also like being able to just set the id, and not fetch the entire related entity.
Is that a good way to do it ? And is it still the only way to do it ?
I am also in the process of figuring out typeorm. What I found out is that you can do:
product.category = <any>3;
// or
product['category' as any] = 3;
repository.save(product) // I don't know how you have the persist() method.
and, in your case, the product.categoryId column will be set to 3. If categoryId is a foreign key and you set a non-existing id, you will get a foreign key error, like you should.
But this way ts will still think that product.category is of type Category. You can also specify the category property as a Category | number. But then you would have to do type checks everywhere which is annoying. I've tested this a bit, but I'm not sure if this will cause some unsuspecting bugs.
What does the name have to do with that ??
Well the option you provided is to define 2 properties: category which is a relation, and categoryId which is the column. The property categoryId should be named like the column in the table, but you can also pass the name: 'actual_name' in the #Column decorator. I don't know what happens if you set both columnId and the column properties with different ids.
According to this GitHub thread, it seems you can also do something like this:
product.category = { id: 1 }
product.save()
// Or
product.category = new Category().id = 1
product.save()
I have the following table structure that mixes legacy fields with an updated schema:
Coaster
Id (Int)
ParkId (Int)
Park
Id (int)
UniqueId (Guid)
So, the Coaster and Park tables are linked using the Park.Id field. The UniqueId field is not currently used in the old schema. We are migrating to a clean DB using AutoMapper, and this new schema looks like this:
Coaster
Id (Int)
ParkId (Guid)
Park
Id (Guid)
The problem I am having is doing this using AutoMapper. I've been experimenting with this code:
private ModelMapper()
{
Mapper.Initialize(x =>
{
x.AddProfile<ConvertersMappingProfile>();
});
}
// This is the part that I'm trying to work out
public ConvertersMappingProfile()
{
CreateMap<Park, NewSchema.Park>()
.ForMember(dst => dst.Id, map => map.MapFrom(src => src.ParkId));
}
In the new schema, the Park table's Id matches the old schema's UniqueId.
My question is: Because in the old schema there is no direct link to the UniqueId value of the Park table, how to do I get that value to map to the new schema using the Coaster.ParkId field to Park.Id field mapping?
I used a custom resolve to fix the problem. So I created my resolver, which pulls up a list of the items in the database (which is typically pretty small) to get all the values from the table I need, and retrieves the value. (Pre-refactored code):
public class ParkResolver : IValueResolver<Original.Park, New.Park, string> {
public string Resolve(Original.Park source, New.Park dest, string destMember, ResolutionContext context) {
List<Original.Park> list = new List<Original.Park>();
using (IDbConnection con = new SQLiteConnection(#"Data Source=C:\Users\Me\Documents\Parks.sql;")) {
con.Open();
list = con.Query<Original.Park>($"SELECT * FROM Parks WHERE Id = {source.ParkId}").ToList();
con.Close();
}
return list.FirstOrDefault().UniqueId;
}
}
And I call my custom resolver for the tables I am mapping:
CreateMap<Original.Park, New.Park>()
.ForMember(dst => dst.ParkId, map => map.MapFrom(new ParkResolver()));
Hopefully that will help someone else.
Was hoping to get some help with this since I've tried everything and the docs are basically completely useless when trying to figure out what to do here.
Essentially, I have a model called Address, which contains all the fields you'd expect.
This auto creates a table called Addresses and by itself works fine. However, I also want to make an "Address_History" table that uses the same model.
Basically, I want this so that in my route if a user enters a new address, it stores the old address in the history table and then puts the new one inside the addresses table.
I can do the actual code for this, but I just can't get this damn association to work in a way like I said above in regards to having 2 separate tables using the same model.
I've tried everything including the as keyword and all that seems to do is add an extra column, not a table.
Basically I want something like this (But working);
User.hasOne(Address, {
as: 'Address',
});
Address.belongsTo(User, {
as: 'Address',
});
User.hasMany(Address, {
as: 'addressHistory',
});
Address.belongsTo(User, {
as: 'addressHistory',
});
Any help is greatly appreciated.
The as option in hasMany() function does not create a new table in your DB. To create a new table, you need to call init() or define(), which are static functions. However, you can keep your codes pretty much DRY by modularizing the ctor and options. For example,
const AddressCommonCtor = {
// your common column fields here
};
const AddressCommonOptions = {
// options here
// Make sure `modelName` field is not included here
// because you want to create two different tables with different names in your DB
};
class Address extends Model {};
class AddressHistory extends Model {};
Address.init(AddressCommonCtor, AddressCommonOptions);
AddressHistory.init(AddressCommonCtor, AddressCommonOptions);
Then you can associate Address and AddressHistory with User like this:
User.hasOne(Address, {
as: 'address',
});
User.hasMany(AddressHistory, {
as: 'addressHistory',
});