Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to turn off the the creation of specific types for primary key fields? #77

Closed
boggye opened this issue Dec 31, 2023 · 8 comments

Comments

@boggye
Copy link

boggye commented Dec 31, 2023

Hi,

I have the following table:

CREATE TABLE school
(
  school_id Integer NOT NULL GENERATED ALWAYS AS IDENTITY
    (INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1),
  school_name Character varying(250) NOT NULL,
  created_dt Timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
  last_modified_dt Timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
)
WITH (
  autovacuum_enabled=true)
;

ALTER TABLE school ADD CONSTRAINT pk_school PRIMARY KEY (school_id)
;

Typo generates the following case class & companion object:

case class SchoolId(value: Int) extends AnyVal
object SchoolId {
  implicit lazy val arrayColumn: Column[Array[SchoolId]] = Column.columnToArray(column, implicitly)
  implicit lazy val arrayToStatement: ToStatement[Array[SchoolId]] = typo.IntArrayToStatement.contramap(_.map(_.value))
  implicit lazy val column: Column[SchoolId] = Column.columnToInt.map(SchoolId.apply)
  implicit lazy val ordering: Ordering[SchoolId] = Ordering.by(_.value)
  implicit lazy val parameterMetadata: ParameterMetaData[SchoolId] = new ParameterMetaData[SchoolId] {
    override def sqlType: String = ParameterMetaData.IntParameterMetaData.sqlType
    override def jdbcType: Int = ParameterMetaData.IntParameterMetaData.jdbcType
  }
  implicit lazy val reads: Reads[SchoolId] = Reads.IntReads.map(SchoolId.apply)
  implicit lazy val text: Text[SchoolId] = new Text[SchoolId] {
    override def unsafeEncode(v: SchoolId, sb: StringBuilder) = Text.intInstance.unsafeEncode(v.value, sb)
    override def unsafeArrayEncode(v: SchoolId, sb: StringBuilder) = Text.intInstance.unsafeArrayEncode(v.value, sb)
  }
  implicit lazy val toStatement: ToStatement[SchoolId] = ToStatement.intToStatement.contramap(_.value)
  implicit lazy val writes: Writes[SchoolId] = Writes.IntWrites.contramap(_.value)
}

Is it possible to turn off the creation of this class and use only a straight Int?

If you can point me to an article that describes this approach in detail, that would be great.

Thanks

@oyvindberg
Copy link
Owner

You can use typeOverride, see https://oyvindberg.github.io/typo/docs/customization/customize-types/

val options = Options(
  // ...
  typeOverride = TypeOverride.relation {
    case ("public.identity-test", "name") => "String"
  }
)

What's the problem with the primary key types by the way? I consider it such a cool feature to get those for free

@oyvindberg
Copy link
Owner

I mean I could expose a better interface for this, similar to Options#readonlyRepo which uses a Selector to make a decision.

@boggye
Copy link
Author

boggye commented Dec 31, 2023

What's the problem with the primary key types by the way? I consider it such a cool feature to get those for free

I feel it adds a lot of unnecessary noise to the code. My app is a simple CRUD app, I am the only developer, and I don't think I am going to trip over myself if I use Int fields instead of value types fields. I know that there are other people that exult the benefits of using specific types and I am not negating those benefits in other contexts. I was wondering if there is an option to turn them off and use plain types.

@boggye
Copy link
Author

boggye commented Jan 1, 2024

as you per your suggestion I used the following setting and I was able to change the scala types of the these fields to Int:

import typo.db.RelationName

val rewriteMore = TypeOverride.of {
  case (RelationName(Some(schema), tableName), colName) if colName.value.endsWith("_id") => "Int"
}

Another question: is it possible to re-map a scala type by sql type name not by sql field name as it is done above? For instance, I want to map Date sql fields to LocalDate in scala and timestampz fields to ZonedDateTime regardless the name of the field. Put it another way, inside the partial function above is it possible to have access to the sql type of the column?

oyvindberg added a commit that referenced this issue Jan 1, 2024
@oyvindberg
Copy link
Owner

I added the possibility of not generating id types for the relations you specify in #83 .

Another question: is it possible to re-map a scala type by sql type name not by sql field name as it is done above? For instance, I want to map Date sql fields to LocalDate in scala and timestampz fields to ZonedDateTime regardless the name of the field. Put it another way, inside the partial function above is it possible to have access to the sql type of the column?

No, not now. I also want to get rid of the TypoLocalDate and TypoInstant types, but it needs more work. it's a bit harder than it looks. I think the best way forward for now if you don't want these types leaking into your business layer is to duplicate the row types into proper domain model types and maintain explicit mapping. This is likely the best choice regardless of these types.

Also note that ZonedDateTime is the wrong data type - you lose the zone when you roundtrip it.

@boggye
Copy link
Author

boggye commented Jan 1, 2024

Thank you for the prompt response.

Also note that ZonedDateTime is the wrong data type - you lose the zone when you roundtrip it.

Do you have an example that shows what's wrong? From the little testing i've done the type seems ok. When you say "roundtrip" do you mean: get the value from the db and then save it to the database, and the process of saving removes the TZ from the value?

Thanks again!

@oyvindberg
Copy link
Owner

oyvindberg commented Jan 1, 2024

get the value from the db and then save it to the database, and the process of saving removes the TZ from the value?

Create it in code, persist it to pg and read it back. timestamptz stores the timestamp as UTC, so the original time zone is lost. Either postgres or the postgres driver may rewrite it back to its current time zone for you. if that's the case it'll look alright and you'll need to create the original with a time zone different than your current one to see it

@boggye
Copy link
Author

boggye commented Jan 1, 2024

ok, thank you for the explantions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants