|
| 1 | +# Getting started |
| 2 | + |
| 3 | +## Pre-requisites |
| 4 | +### Install LibPQ.jl |
| 5 | +`MyProject> add LibPQ` |
| 6 | + |
| 7 | +### Install PostgresORM.jl |
| 8 | +`MyProject> add https://github.com/JuliaPostgresORM/PostgresORM.jl` |
| 9 | + |
| 10 | +## Example projects |
| 11 | +You can look at the following projects to see how PostgresORM is used : |
| 12 | + * [IMDBTestApp.jl](https://github.com/JuliaPostgresORM/IMDBTestApp.jl) |
| 13 | + |
| 14 | +## Concepts |
| 15 | + |
| 16 | +### Classes |
| 17 | + |
| 18 | +PostgreSQL tables are mapped to mutable composite types that inherit the |
| 19 | +abstract type PostgresORM.IEntity. |
| 20 | + |
| 21 | +For the sake of conciseness we call this particular type a _class_. |
| 22 | + |
| 23 | +A _class_ looks like this : |
| 24 | + |
| 25 | +``` |
| 26 | +mutable struct Film <: IFilm |
| 27 | +
|
| 28 | + id::Union{Missing,Int32} |
| 29 | + codeName::Union{Missing,String} |
| 30 | + year::Union{Missing,Int16} |
| 31 | + actorFilmAssos::Union{Missing,Vector{Model.IActorFilmAsso}} |
| 32 | +
|
| 33 | + Film(args::NamedTuple) = Film(;args...) |
| 34 | + Film(; |
| 35 | + id = missing, |
| 36 | + codeName = missing, |
| 37 | + year = missing, |
| 38 | + actorFilmAssos = missing, |
| 39 | + ) = ( |
| 40 | + x = new(missing,missing,missing,missing,); |
| 41 | + x.id = id; |
| 42 | + x.codeName = codeName; |
| 43 | + x.year = year; |
| 44 | + x.actorFilmAssos = actorFilmAssos; |
| 45 | + return x |
| 46 | + ) |
| 47 | +
|
| 48 | +end |
| 49 | +``` |
| 50 | + |
| 51 | +Lets describe the key aspects of a _class_: |
| 52 | + |
| 53 | +#### A _class_ inherits an abstract type that inherits IEntity |
| 54 | +`mutable struct Film <: IFilm` where `IFilm <: PostgresORM.IEntity` |
| 55 | + |
| 56 | +This allows us to avoid circular dependencies |
| 57 | + |
| 58 | +#### Fields of a class are all Union of a Missing and something else |
| 59 | +`id::Union{Missing,Int32}` |
| 60 | + |
| 61 | +The 'something else' can be a lot of things including a `IEntity` or a vector of |
| 62 | +`IEntity`. |
| 63 | + |
| 64 | +In this documentation we call |
| 65 | + * A '_complex property_', a property of type `IEntity`. It is also named |
| 66 | + a "manyToOne" property and it resolves to a foreign key in the table of the |
| 67 | + _class_. |
| 68 | + * A '_property of IEntities_', a property of type |
| 69 | + `Vector{T} where T <: IEntity`. It is also named a "oneToMany" property and |
| 70 | + it is the counter part of a _complex property_ in another _class_. |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | +#### A _class_ has two constructors |
| 75 | + |
| 76 | + 1. A constructor that takes a NamedTuple and that is required by PostgresORM |
| 77 | +function. It calls the second constructor by splatting the NamedTuple. |
| 78 | + 2. A constructor that takes optional named arguments with default values |
| 79 | + `missing` and that assign the values to the matching properties. |
| 80 | + |
| 81 | +Therefore: |
| 82 | + |
| 83 | + * Calling `Film()` creates an instance of _Film_ with all properties set |
| 84 | +to `missing` |
| 85 | + * Calling `Film(id = 34, codeName = "cube")` creates an instance of _Film_ |
| 86 | +with all properties set to `missing` except _id_ and _codeName_ |
| 87 | + |
| 88 | +### ORM modules |
| 89 | +An ORM module is a Julia module that tells PostgresORM how to handle a _class_. |
| 90 | +It contains the following: |
| 91 | + |
| 92 | + * `data_type = Model.Film`: Assigns the module variable `data_type` to the |
| 93 | + _class_ associated with the ORM module |
| 94 | + * `PostgresORM.get_orm(x::Model.Film) = return(ORM.FilmORM)`: Declares a new |
| 95 | + method of function `PostgresORM.get_orm`, this function is used to tell |
| 96 | + PostgresORM which ORM module to use for a given _class_ |
| 97 | + * `get_schema_name() = "public"`: Returns the PostgreSQL schema name of the |
| 98 | +table associated with the _class_ |
| 99 | + * `get_table_name() = "film"`: Returns the table name associated with the _class_ |
| 100 | + * `get_columns_selection_and_mapping()`: Returns the mapping between julia |
| 101 | + fields and table columns. Note that a _complex property_ can be mapped to |
| 102 | + an array of columns if the foreign key has multiple columns (i.e. if the |
| 103 | + _class_ of the _complex_property_ has a composite id) |
| 104 | + * `get_id_props()`: Returns the fields that make the id of the _class_. These |
| 105 | + fields can be _complex properties_ |
| 106 | + * `get_onetomany_counterparts()`: It gives for every _property of IEntities_ |
| 107 | + the associated _complex property_ (i.e. manyToOne property) |
| 108 | + * `get_types_override()`: It gives for every oneToMany or manyToOne property |
| 109 | + the real type of the property |
| 110 | + |
| 111 | +Some optional functions for the tracking of changes: |
| 112 | + * `get_track_changes()`: Tells PostgresORM to record all the changes made to |
| 113 | + instances of the _class_ |
| 114 | + * `get_creator_property()`: Tells which property holds the reference of the |
| 115 | + user that created the instance. This property must inherit `PostgresORM.AppUser` |
| 116 | + * `get_editor_property()`: Tells which property holds the reference of the |
| 117 | + user that last edited the instance. This property must inherit `PostgresORM.AppUser` |
| 118 | + * `get_creation_time_property()`: Tells which property holds the creation |
| 119 | + time of the instance |
| 120 | + * `get_update_time_property()`: Tells which property holds the last update |
| 121 | + time of the instance |
| 122 | + |
| 123 | +### Enums |
| 124 | +Julia enums are the counterpart of PostgreSQL custom enumeration types. |
| 125 | + |
| 126 | +### LibPQ connection |
| 127 | +Many PostgresORM functions expects a `LibPQ.Connection` as one of the arguments. |
| 128 | +The developer is in charge of managing the connections and the transactions. |
| 129 | + |
| 130 | +## Reverse engineer the database |
| 131 | +The easiest way to get started is to ask PostgresORM to generate the _classes_, |
| 132 | +the ORM modules and the enums. Once done, you can copy the files in the _src_ |
| 133 | +folder of the project and declare everything in the project |
| 134 | +(see how it's done in |
| 135 | +[IMDBTestApp.jl](https://github.com/JuliaPostgresORM/IMDBTestApp.jl)). |
| 136 | + |
| 137 | +Here is an example of script to reverse engineer a database: |
| 138 | + |
| 139 | +``` |
| 140 | +out_dir = (@__DIR__) * "/out" |
| 141 | +dbconn = begin |
| 142 | + database = "imdbtestapp" |
| 143 | + user = "imdbtestapp" |
| 144 | + host = "127.0.0.1" |
| 145 | + port = "5432" |
| 146 | + password = "1234" |
| 147 | +
|
| 148 | + LibPQ.Connection("host=$(host) |
| 149 | + port=$(port) |
| 150 | + dbname=$(database) |
| 151 | + user=$(user) |
| 152 | + password=$(password) |
| 153 | + "; throw_error=true) |
| 154 | + end |
| 155 | +PostgresORM.Tool.generate_julia_code(dbconn,out_dir) |
| 156 | +
|
| 157 | +close(dbconn) |
| 158 | +``` |
0 commit comments