- D 100%
| .forgejo/workflows | ||
| .vscode | ||
| source/ninox/oxm | ||
| subpackages | ||
| test | ||
| testing | ||
| .gitignore | ||
| dub.json | ||
| dub.selections.json | ||
| LICENSE | ||
| readme.md | ||
ninox.d-oxm
A complete oxm (orm & odm) for dlang projects
License
The code in this repository is licensed under AGPL-3.0-or-later; for more details see the LICENSE file in the repository.
Example
module app;
import ninox.oxm;
import ninox.oxm.entities.generators.sequence;
// Explicitly import the backend's module in order for it to be available
import ninox.oxm.backend_libmariadb;
// @Entity is needed to discover this entity
@Entity
// @Storage is an optional UDA to specify the storage name of the entity
// this name gets used to create the table / collection inside the database
@Storage("person")
class Person {
// With @Id fields get annotated as primary key(s); composite keys can be created
// by annotating multiple fields with it
@Id
// @GeneratedValue tells ninox.d-oxm that the value of this field is to be generated
// on insertion. It needs a argument which represents a function which returns a instance of ValueGenerator.
@GeneratedValue([] { return new SequenceGenerator!int("seq_person"); })
int id;
// Normaly, ninox.d-oxm dosnt need a @Field annotation and tries to deduce the used type in the
// database from the native type, and uses the field's name as name inside the database.
// But sometimes you need to change things a bit or to specify some other parameters for the type
// such as maximum length; for this one can use the @Field UDA.
@Field(FieldType.String, "fullname", 255)
string name;
int age;
// To exclude fields from being persisted, annotate them with @IgnoreField
@IgnoreField
int some_tmp_field;
// Mixin needed in order to generate the code neccessary for ninox.d-oxm.
// It also needs the current entity as generic parameter.
mixin BaseEntity!Person;
}
void main() {
// First, you need to create an Connection; this can be done by calling Connection.create.
// It also accepts an list of generic arguments representing modules of your app
// where ninox.d-oxm should search after entities (annotated by @Entity).
auto con = Connection.create!app(
"mysql:host=<hostname>;db_name=<dbname>", "<db user>", "<db password>"
);
// When want to use entities dynamically onto connections,
// you also can use following method to create the table of the entity:
Person.NinoxOxmModel.ensurePresence(con);
// With <Entity>.find(), you can start a query
auto q = Person.find();
// On this query, you can do various things, such as filtering for
// specific values / conditions on fields / columns.
q.filter!"name"("Max");
// You also can specify offsets, limits and even ordering
q.limit(12);
q.offset(0);
q.order(Order.Asc);
// To execute the query you'll need to run the all() or one() method;
// the first returns an array with all entities matched while the later
// restricts the query to one element and returns an `Optional!T` where T is
// the entity in question.
auto list = q.all(con);
foreach (p; list) {
writeln("name: `", p.name, "` | ", "age: ", p.age, "`");
}
{
// To insert a entity, construct the object like normal...
auto p = new Person();
p.name = "Max";
p.age = 30;
// ... then call insert() to get the insertion query ...
auto q = p.insert();
// ... and finally call exec() with your connection to execute it.
// This also calls all value generators for fields with generated values.
q.exec(con);
}
{
// To update a exisiting entity, use the update() method instead to create
// an update query.
Person p = /* ... */;
p.update().exec(con);
}
{
// To delete a exisiting entity, use the del() method instead to create
// an delete query.
Person p = /* ... */;
p.del().exec(con);
}
}
Relations
Be aware that currently ninox.d-oxm has not way of simply calling something like save() to do all persisting steps for you.
HasOne
Used to add the referenceing side of a relation:
@Entity class User {
@Id @GeneratedValue([] { return new SequenceGenerator!int("user"); })
int id;
mixin has_one!(Profile, "profile", ["id"], ["userId"], "user");
// Alternative syntax:
// mixin relation!(User, HasOne( name: "profile", fields: ["id"], references: ["userId"], inverseOf: "user" ));
}
@Entity class Profile {
@Id @GeneratedValue([] { return new SequenceGenerator!int("profile"); })
int id;
int userId;
mixin belongs_to!(User, "user", ["userId"], ["id"], "profile");
}
Make sure you persist the non-owning side first:
User u = new User();
u.profile = new Profile();
u.insert().exec(con); // sets automatically Profile.userId
u.profile.insert().exec(con);
BelongsTo
Used to create the owning side of a relation & persist it into the entity:
@Entity class User {
@Id @GeneratedValue([] { return new SequenceGenerator!int("user"); })
int id;
mixin has_one!(Profile, "profile", ["id"], ["userId"], "user");
}
@Entity class Profile {
@Id @GeneratedValue([] { return new SequenceGenerator!int("profile"); })
int id;
int userId;
mixin belongs_to!(User, "user", ["userId"], ["id"], "profile");
// Alternative syntax:
// mixin relation!(User, BelongsTo( name: "user", fields: ["userId"], references: ["id"], inverseOf: "profile" ));
}
Note: you also can persist a 1-to-1 relation on both sides by replacing the has_one with a belongs_to, so both sides have it.
When using entities that persist on both sides, you need to choose one that will be created first, then create the other and finnaly update the first again so the database has the correct values.
User u = new User();
u.profile = new Profile();
// persists the Profile, updates User.profileId accordingly
u.profile.insert().exec(con);
// persists the User, updates Profile.userId accordingly
u.insert().exec(con);
// Updates the changed Profile.userId inside the database
u.profile.update().exec(con);