2
votes

I'm using typeorm v 0.1.14.

typeorm seems to be generating the same alias name for two different columns in a Many-to-One join. It doesn't matter if I use eager loading in the @ManyToOne decorator (which loads the child using .find() method on the repository, e.g.) or if I use the query builder functionality to load the relation.

The simplest example I could recreate this with is below.

// SampleStatus entity/file:
import { Entity, Column, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';

@Entity({name: 'statuses', schema: 'master'})
export class SampleStatus {

    @PrimaryGeneratedColumn()
    public id: number;
}

// Sample entity that has a child statusimport { Entity, Column, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { SampleStatus } from './sample-status.entity';

@Entity({ name: 'samples', schema: 'master'})
export class Sample {

    @PrimaryGeneratedColumn()
    public id: number;

    @Column()
    public name: string;

    @ManyToOne(type => SampleStatus, status => status.id, { eager: true })
    public status: SampleStatus;
}

Using those entities above in the simplest way possible: let result: Sample[] | null = await getRepository(Sample).find() || [];

This generates the following logged query, formatted by me:

SELECT "Sample"."id" AS "Sample_id", 
  "Sample"."name" AS "Sample_name", 
  "Sample"."status_id" AS "Sample_status_id", 
  "Sample_status"."id" AS "Sample_status_id" 
FROM "master"."samples" "Sample" 
  LEFT JOIN "master"."statuses" "Sample_status" 
    ON "Sample_status"."id"="Sample"."status_id"

Note how the two names are generated-- they are both the same aliased column name.

I am using this bit of code that I found, which converts the entity property names to be snake-cased. This gets passed into the connection set-up.

import { DefaultNamingStrategy, NamingStrategyInterface } from "typeorm";
import { snakeCase } from 'typeorm/util/StringUtils'

export class SnakeCaseNamingStrategy extends DefaultNamingStrategy  {

    tableName(targetName: string, userSpecifiedName: string): string {
        return userSpecifiedName ? userSpecifiedName : snakeCase(targetName);
    }

    columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string {
        return snakeCase(embeddedPrefixes.concat(customName ? customName : propertyName).join("_"));
    }

    columnNameCustomized(customName: string): string {
        return customName;
    }

    relationName(propertyName: string): string {
        return snakeCase(propertyName);
    }

    joinTableName(firstTableName: string, secondTableName: string, firstPropertyName: string, secondPropertyName: string): string {
        return `${snakeCase(firstTableName)}_${secondTableName}`;
    }
}

While the value being pulled back is the same (since it's a join column) and doesn't really cause an issue when running, I noticed this when taking the logged query and turning it into a view. Postgres complained about the duplicate column name.

Is there a way to either alias the second select into a different name, or perhaps not select the second value at all?

3

3 Answers

3
votes

This is a known TypeORM bug for databases using the snake case naming strategy which is apparently to be fixed in their 0.3.0 release (see this thread: https://github.com/typeorm/typeorm/issues/2200)

For now, you can get around this by defining your naming strategy as follows:

const SnakeNamingStrategy = require('typeorm-naming-strategies').SnakeNamingStrategy;

module.exports = {
   type: ...,
   host: ...,
   port: ...,
   ...
   namingStrategy: new SnakeNamingStrategy(),
}

Which is a bit nicer to work with than your current work around

0
votes

I am facing the same issue, its sad that nobody fixed this bug already. I found an ugly workaround for this issue : you have to use different name of the column than class name you are referring to.

0
votes

It's because TypeORM currently set's that column to be unique, you only have to specify column's unique option to false.