Skip to content

Commit

Permalink
#79 Embedded Annotation with Optional Column Prefix (#81)
Browse files Browse the repository at this point in the history
* Added @Embedded annotation and use prefix for table creation. Next: Fix errors on SELECT.

* Added logic for SELECT columns to respect embedded prefixes.

* Added @Embedded prefix support for insert/update.

* Add documentation of functions explored.

* Organizing tests, adding more tests for embedded.

* Complete test coverage for embedded logic.

* Fix for MYSQL compilation flag in integration tests.

* One more fix for MySQL tests.

* Sigh... one more typo on Postgres config.

* Remove TODO statements that were left behind.
  • Loading branch information
vnayar authored Nov 26, 2023
1 parent c072096 commit b810b14
Show file tree
Hide file tree
Showing 11 changed files with 694 additions and 323 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ examples/example1/example1

# Ignore Sublime Text workspace (project file is ok):
*.sublime-workspace

# Emacs/Vim
*~
\#*\#
1 change: 1 addition & 0 deletions hdtest/dub.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"hibernated": {"version": "~master", "path": "../"}
},
"buildRequirements": ["allowWarnings"],
"mainSourceFile": "source/htestmain.d",
"configurations": [
{
"name": "full",
Expand Down
116 changes: 116 additions & 0 deletions hdtest/source/embeddedtest.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
module embeddedtest;

import hibernated.core;

import testrunner : BeforeClass, Test, AfterClass, runTests;
import hibernatetest : HibernateTest;

@Embeddable
class Address {
string street;
string city;
}

class Customer {
@Id @Generated
long cid;

string name;

Address shippingAddress;

@Embedded("billing")
Address billingAddress;
}

class EmbeddedTest : HibernateTest {
override
EntityMetaData buildSchema() {
return new SchemaInfoImpl!(Customer, Address);
}

@Test("embedded.creation")
void creationTest() {
Session sess = sessionFactory.openSession();
scope(exit) sess.close();

Customer c1 = new Customer();
c1.name = "Kickflip McOllie";
c1.shippingAddress = new Address();
c1.shippingAddress.street = "1337 Rad Street";
c1.shippingAddress.city = "Awesomeville";
c1.billingAddress = new Address();
c1.billingAddress.street = "101001 Robotface";
c1.billingAddress.city = "Lametown";

long c1Id = sess.save(c1).get!long;
assert(c1Id > 0);
}

@Test("embedded.read")
void readTest() {
Session sess = sessionFactory.openSession();
scope(exit) sess.close();

auto r1 = sess.createQuery("FROM Customer WHERE shippingAddress.city = :City")
.setParameter("City", "Awesomeville");
Customer c1 = r1.uniqueResult!Customer();
assert(c1 !is null);
assert(c1.shippingAddress.street == "1337 Rad Street");

auto r2 = sess.createQuery("FROM Customer WHERE billingAddress.city = :City")
.setParameter("City", "Lametown");
Customer c2 = r2.uniqueResult!Customer();
assert(c2 !is null);
assert(c2.billingAddress.street == "101001 Robotface");
}

@Test("embedded.update")
void updateTest() {
Session sess = sessionFactory.openSession();

auto r1 = sess.createQuery("FROM Customer WHERE billingAddress.city = :City")
.setParameter("City", "Lametown");
Customer c1 = r1.uniqueResult!Customer();
assert(c1 !is null);

c1.billingAddress.street = "17 Neat Street";
sess.update(c1);

// Create a new session to prevent caching.
sess.close();
sess = sessionFactory.openSession();

r1 = sess.createQuery("FROM Customer WHERE billingAddress.city = :City")
.setParameter("City", "Lametown");
c1 = r1.uniqueResult!Customer();
assert(c1 !is null);
assert(c1.billingAddress.street == "17 Neat Street");

sess.close();
}

@Test("embedded.delete")
void deleteTest() {
Session sess = sessionFactory.openSession();

auto r1 = sess.createQuery("FROM Customer WHERE billingAddress.city = :City")
.setParameter("City", "Lametown");
Customer c1 = r1.uniqueResult!Customer();
assert(c1 !is null);

sess.remove(c1);

// Create a new session to prevent caching.
sess.close();
sess = sessionFactory.openSession();

r1 = sess.createQuery("FROM Customer WHERE billingAddress.city = :City")
.setParameter("City", "Lametown");
c1 = r1.uniqueResult!Customer();
assert(c1 is null);

sess.close();
}

}
248 changes: 248 additions & 0 deletions hdtest/source/generaltest.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
module generaltest;

import std.typecons;
import std.stdio;
import std.format;
import std.conv;

import hibernated.core;

import testrunner : BeforeClass, Test, AfterClass, runTests;
import hibernatetest : HibernateTest;

// Annotations of entity classes
@Table( "gebruiker" )
class User {
long id;
string name;
int some_field_with_underscore;
@ManyToMany // cannot be inferred, requires annotation
LazyCollection!Role roles;
//@ManyToOne
MyGroup group;

@OneToMany
Address[] addresses;

Asset[] assets;

override string toString() {
return format("{id: %s, name: %s, roles: %s, group: %s}",
id, name, roles, group);
}
}

class Role {
int id;
string name;
@ManyToMany // w/o this annotation will be OneToMany by convention
LazyCollection!User users;

override string toString() {
return format("{id: %s, name: %s}", id, name);
}
}

class Address {
@Generated @Id int addressId;
User user;
string street;
string town;
string country;

override string toString() {
return format("{id: %s, user: %s, street: %s, town: %s, country: %s}", addressId, user, street, town, country);
}
}

class Asset {
@Generated @Id int id;
User user;
string name;

override string toString() {
return format("{id: %s, name: %s}", id, name);
}
}

@Entity
class MyGroup {
long id;
string name;
@OneToMany
LazyCollection!User users;

override string toString() {
return format("{id: %s, name: %s}", id, name);
}
}

class GeneralTest : HibernateTest {
override
EntityMetaData buildSchema() {
return new SchemaInfoImpl!(User, Role, Address, Asset, MyGroup);
}

@Test("general test")
void generalTest() {
// create session
Session sess = sessionFactory.openSession();
scope(exit) sess.close();

// use session to access DB

writeln("Querying empty DB");
Query q = sess.createQuery("FROM User ORDER BY name");
User[] list = q.list!User();
writeln("Result size is " ~ to!string(list.length));
assert(list.length == 0);

// create sample data
writeln("Creating sample schema");
MyGroup grp1 = new MyGroup();
grp1.name = "Group-1";
MyGroup grp2 = new MyGroup();
grp2.name = "Group-2";
MyGroup grp3 = new MyGroup();
grp3.name = "Group-3";

Role r10 = new Role();
r10.name = "role10";
Role r11 = new Role();
r11.name = "role11";

// create a user called Alex with an address and an asset
User u10 = new User();
u10.name = "Alex";
u10.roles = [r10, r11];
u10.group = grp3;

auto address = new Address();
address.street = "Some Street";
address.town = "Big Town";
address.country = "Alaska";
address.user = u10;
writefln("Saving Address: %s", address);
sess.save(address);

u10.addresses = [address];

auto asset = new Asset();
asset.name = "Something Precious";
asset.user = u10;
writefln("Saving Asset: %s", asset);
sess.save(asset);
u10.assets = [asset];

User u12 = new User();
u12.name = "Arjan";
u12.roles = [r10, r11];
u12.group = grp2;

User u13 = new User();
u13.name = "Wessel";
u13.roles = [r10, r11];
u13.group = grp2;

writeln("saving group 1-2-3" );
sess.save( grp1 );
sess.save( grp2 );
sess.save( grp3 );

writeln("Saving Role r10: " ~ r10.name);
sess.save(r10);

writeln("Saving Role r11: " ~ r11.name);
sess.save(r11);

{
writeln("Saving User u10: " ~ u10.name ~ "...");
long id = sess.save(u10).get!long;
assert(id > 0L);
writeln("\tuser saved with id: " ~ to!string(id));
}

{
writeln("Saving User u12: " ~ u12.name ~ "...");
long id = sess.save(u12).get!long;
assert(id > 0L);
writeln("\tuser saved with id: " ~ to!string(id));
}

{
writeln("Saving User u13: " ~ u13.name ~ "...");
long id = sess.save(u13).get!long;
assert(id > 0L);
writeln("\tuser saved with id: " ~ to!string(id));
}

writeln("Querying User by name 'Alex'...");
// load and check data
auto qresult = sess.createQuery("FROM User WHERE name=:Name and some_field_with_underscore != 42").setParameter("Name", "Alex");
writefln( "\tquery result: %s", qresult.listRows() );
User u11 = qresult.uniqueResult!User();
writefln("\tChecking fields for User 11 : %s", u11);
assert(u11.roles.length == 2);
assert(u11.roles[0].name == "role10" || u11.roles.get()[0].name == "role11");
assert(u11.roles[1].name == "role10" || u11.roles.get()[1].name == "role11");
assert(u11.roles[0].users.length == 3);
assert(u11.roles[0].users[0] == u10);

assert(u11.addresses.length == 1);
assert(u11.addresses[0].street == "Some Street");
assert(u11.addresses[0].town == "Big Town");
assert(u11.addresses[0].country == "Alaska");

assert(u11.assets.length == 1);
assert(u11.assets[0].name == "Something Precious");

// selecting all from address table should return a row that joins to the user table
auto allAddresses = sess.createQuery("FROM Address").list!Address();
assert(allAddresses.length == 1);
writefln("Found address : %s", allAddresses[0]);
assert(allAddresses[0].street == "Some Street");
assert(allAddresses[0].user == u11);

// selecting all from asset table should return a row that joins to the user table
auto allAssets = sess.createQuery("FROM Asset").list!Asset();
assert(allAssets.length == 1);
writefln("Found asset : %s", allAssets[0]);
assert(allAssets[0].name == "Something Precious");
assert(allAssets[0].user == u11);

// now test something else
writeln("Test retrieving users by group... (ManyToOne relationship)");
auto qUsersByGroup = sess.createQuery("FROM User WHERE group=:group_id").setParameter("group_id", grp2.id);
User[] usersByGroup = qUsersByGroup.list!User();
assert(usersByGroup.length == 2); // user 2 and user 2

{
writeln("Updating User u10 name (from Alex to Alexander)...");
u10.name = "Alexander";
sess.update(u10);

User u = sess.createQuery("FROM User WHERE id=:uid")
.setParameter("uid", u10.id)
.uniqueResult!User();
assert(u.id == u10.id);
assert(u.name == "Alexander");
}

// remove reference
//std.algorithm.remove(u11.roles.get(), 0);
//sess.update(u11);

{
auto allUsers = sess.createQuery("FROM User").list!User();
assert(allUsers.length == 3); // Should be 3 user nows
}
writeln("Removing User u11");
sess.remove(u11);

{
auto allUsers = sess.createQuery("FROM User").list!User();
assert(allUsers.length == 2); // Should only be 2 users now
}
}
}

Loading

0 comments on commit b810b14

Please sign in to comment.