Relation Missmatch when following dbo::tutorial7
Added by Christian Meyer almost 2 years ago
Hello =)
Following Tutorial7 to use another type as key results in the following error
Error: Wt::Dbo::id() called for class C with surrogate key: Wt::Dbo::dbo_traits::surrogateIdField() != 0
Relation mismatch for table 'ContactForm-Address':
no matching belongsTo() found in table 'ContactForm-Location' with name 'ContactForm-rel-Address_Location'
Tried with hasOne and hasMany, with no difference in Error...
Is that a Bug or a Feature?
I also tried to set the relation name the same as the table name, with no luck.
Following is the complete Set of Files that are related to the Missbehaving with excerpts of what I deem most important to diagnose if something is wrong.
Thank you
complete Address.hcomplete Address.h
#ifndef _CONTACTFORM_DBO_CONTACT_ADDRESS_H_
#define _CONTACTFORM_DBO_CONTACT_ADDRESS_H_
#include "../feature_defines.h"
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/collection.h>
#include <Wt/Dbo/ptr.h>
#include <Wt/Dbo/weak_ptr.h>
#include <string>
#include <Wt/WString.h>
#include <optional>
namespace ContactForm
{
namespace Dbo
{
class Contact;
class Location;
/**
* @brief Address Instance in Database
* Consistent of
* Optional Name (to Identify if multiple Addresses used),
* Optional Recipient (to enable Billing Address, Departments, etc.),
* Country,
* Optional Region (Not always Necessary, but might be helpful),
* Optional Code (Post-Codes are not used everywhere),
* City,
* AddressLine1,
* Optional AddressLine2 (additional Information for Postal Service)
*
* Have another Object for Location Data
*
* Address Data for Suggestions can be found here: https://download.geonames.org/export/zip/
*/
class Address : public Wt::Dbo::Dbo<Address>
{
public:
Address();
~Address();
// Getter Functions
// [...]
template <class Action>
void persist(Action &a);
private:
Wt::Dbo::ptr<Contact> contactPtr;
Wt::Dbo::collection<Wt::Dbo::ptr<Location>> locationPtr;
// Wt::Dbo::weak_ptr<Location> locationPtr;
Wt::WString name;
Wt::WString addLine1, country, city;
// enable Null Values for these fields with std::optional maybe
Wt::WString addLine2, region, code, recipient;
};
}
}
DBO_EXTERN_TEMPLATES(ContactForm::Dbo::Address)
#endif // _CONTACTFORM_DBO_CONTACT_ADDRESS_H_
Important Part in Address.h
Wt::Dbo::collection<Wt::Dbo::ptr<Location>> locationPtr;
// Wt::Dbo::weak_ptr<Location> locationPtr;
complete Address.cppcomplete Address.cpp
#include "Address.h"
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/collection.h>
#include <Wt/Dbo/ptr.h>
#include <Wt/Dbo/weak_ptr.h>
#include <Wt/Dbo/WtSqlTraits.h>
#include <Wt/Dbo/StdSqlTraits.h>
DBO_INSTANTIATE_TEMPLATES(ContactForm::Dbo::Address)
#include "Contact.h"
#include "Location.h"
namespace ContactForm
{
namespace Dbo
{
Address::Address()
{}
Address::~Address()
{}
// Getter Functions
// [...]
template <class Action>
void Address::persist(Action &a)
{
Wt::Dbo::field(a, name, "name");
Wt::Dbo::field(a, recipient, "recipient");
Wt::Dbo::field(a, addLine1, "address_line1");
Wt::Dbo::field(a, addLine2, "address_line2");
Wt::Dbo::field(a, country, "country");
Wt::Dbo::field(a, region, "region");
Wt::Dbo::field(a, code, "post_code");
Wt::Dbo::field(a, city, "city");
Wt::Dbo::belongsTo(a, contactPtr, "ContactForm-Contact_Address", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, locationPtr, Wt::Dbo::ManyToOne, "ContactForm-rel-Address_Location");
// Wt::Dbo::hasOne(a, locationPtr, "ContactForm-rel-Address_Location");
}
}
}
Important in Address.cpp
Wt::Dbo::hasMany(a, locationPtr, Wt::Dbo::ManyToOne, "ContactForm-rel-Address_Location");
// Wt::Dbo::hasOne(a, locationPtr, "ContactForm-rel-Address_Location");
complete Location.hcomplete Location.h
#ifndef _CONTACTFORM_DBO_LOCATION_H_
#define _CONTACTFORM_DBO_LOCATION_H_
#include "../feature_defines.h"
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/collection.h>
#include <Wt/Dbo/ptr.h>
#include <Wt/Dbo/weak_ptr.h>
#include <Wt/WDate.h>
#include <Wt/WString.h>
#include <string>
#include <optional>
namespace ContactForm
{
namespace Dbo
{
class Address;
/**
* @brief Enable Additional Information for an Address
*
*/
class Location
{
public:
Location();
~Location();
// Getter Functions
// [...]
template <class Action>
void persist(Action &a);
private:
Wt::Dbo::ptr<Address> addressPtr;
std::optional<Wt::WString> signBell, signMailbox;
std::optional<int> level;
std::optional<bool> elevator;
};
}
}
#endif // _CONTACTFORM_DBO_LOCATION_H_
Important in Location.h
Wt::Dbo::ptr<Address> addressPtr;
complete Location.cppcomplete Location.cpp
#include "Location.h"
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/collection.h>
#include <Wt/Dbo/ptr.h>
#include <Wt/Dbo/weak_ptr.h>
#include <Wt/Dbo/WtSqlTraits.h>
#include <Wt/Dbo/StdSqlTraits.h>
template<>
struct Wt::Dbo::dbo_traits<ContactForm::Dbo::Location> : public Wt::Dbo::dbo_default_traits
{
typedef ptr<ContactForm::Dbo::Address> IdType;
static IdType invalidId() {
return ptr<ContactForm::Dbo::Address>();
}
static const char *surrogateIdField() { return 0; }
};
DBO_INSTANTIATE_TEMPLATES(ContactForm::Dbo::Location)
#include "Address.h"
namespace ContactForm
{
namespace Dbo
{
Location::Location()
{}
Location::~Location()
{}
// Getter Functions
// [...]
template <class Action>
void Location::persist(Action &a)
{
try{
Wt::Dbo::id(a, addressPtr, "ContactForm-rel-Address_Location", Wt::Dbo::OnDeleteCascade);
}
catch(std::exception &e)
{
Wt::log("error") << "Location Persist: id error";
std::cerr << e.what();
}
// optionals
Wt::Dbo::field(a, signBell, "sign_bell");
Wt::Dbo::field(a, signMailbox, "sign_mailbox");
Wt::Dbo::field(a, level, "level");
Wt::Dbo::field(a, elevator, "elevator");
}
}
}
Important in Location.cpp
traits definitiontraits definition
template<>
struct Wt::Dbo::dbo_traits<ContactForm::Dbo::Location> : public Wt::Dbo::dbo_default_traits
{
typedef ptr<ContactForm::Dbo::Address> IdType;
static IdType invalidId() {
return ptr<ContactForm::Dbo::Address>();
}
static const char *surrogateIdField() { return 0; }
};
template <class Action>
void Location::persist(Action &a)
{
try{
Wt::Dbo::id(a, addressPtr, "ContactForm-rel-Address_Location", Wt::Dbo::OnDeleteCascade);
}
catch(std::exception &e)
{
Wt::log("error") << "Location Persist: id error";
std::cerr << e.what();
// >> Error: Wt::Dbo::id() called for class C with surrogate key: Wt::Dbo::dbo_traits::surrogateIdField() != 0
// >> Relation mismatch for table 'ContactForm-Address':
// >> no matching belongsTo() found in table 'ContactForm-Location' with name 'ContactForm-rel-Address_Location'
}
}
Replies (6)
RE: Relation Missmatch when following dbo::tutorial7 - Added by Christian Meyer almost 2 years ago
I now used the example Code to test different things
a OneToOne Relation works in the examples
the name of the relation can be set to whatever...
I really don't understand why it would not work in my own case
If I try to Log the surrogateIdField in my case
Wt::log("info") << "Location::surrogateIdField: "
<< Wt::Dbo::dbo_traits<Location>::surrogateIdField() << ";" ;
it throws basic_string::_M_construct null not valid
which suggests it should work as intended ...
I also tried to set the versionField to something ridiculous to see if that works and logged it, and it does
in my own and in the examples
This Error shows when Calling id in Persist
Error: Wt::Dbo::id() called for class C with surrogate key: Wt::Dbo::dbo_traits::surrogateIdField() != 0
This Error Shows on createSchema
Relation mismatch for table 'ContactForm-Address':
no matching belongsTo() found in table 'ContactForm-Location' with name 'ContactForm-rel-Address_Location'
What am I missing?
RE: Relation Missmatch when following dbo::tutorial7 - Added by Matthias Van Ceulebroeck almost 2 years ago
Hello Christian,
the surrogateIdField
and the Wt::Dbo::id
are currently mutually exclusive.
The documentation does note that surrogateIdField
is to be used for auto-increment primary keys only.
In case a custom Wt::Dbo::id is defined, the surrogateIdField
should be empty (0
or nullptr
).
I have tried your example, but this does not compile. It is important that the
struct Wt::Dbo::dbo_traits<ContactForm::Dbo::Location> : public Wt::Dbo::dbo_default_traits
is defined before any other instantiation of Wt::Dbo::ptr<ContactForm::Dbo::Location>
. Otherwise this does set the default of id
. I suspect this is the case for you, but I do not understand why the compiler doesn't error out on it.
So in short, I would move the dbo_traits
struct to the location.h
, and ensure that it is compiled before any other definition of Wt::Dbo::ptr<ContactForm::Dbo::Location>
.
I hope this helps,
Matthias
RE: Relation Missmatch when following dbo::tutorial7 - Added by Christian Meyer almost 2 years ago
Hello Matthias,
Thank you for taking the time to check
I am aware of the mutually exclusivity, but only run into compiler errors, when the specialization is located in a .h file.
I did try to put the template specialization into Location.h
but then I get the same Compiler Error as in (this forum entry)[[https://redmine.emweb.be/boards/2/topics/18347]] so I moved it after a long try and error conquest.
Craziest thing is, when I try to isolate and include the Dbo classes in an example app, it works as expected.
Apparantly this gets messed up in an entirely different area than the DBO defnitions.
On the other hand, if there are other Classes that need specialization, how would I go about that?
How can I guarantee the order of Compilation?
Can I have your compiler errors for reference? Maybe there is an edge case somewhere in there...
Thank you
RE: Relation Missmatch when following dbo::tutorial7 - Added by Matthias Van Ceulebroeck almost 2 years ago
Ah, I do see that I did one thing slightly differently, and that is adding DBO_EXTERN_TEMPLATES(ContactForm::Dbo::Location)
at the bottom of the location.h
header. If I remove that, this WILL compile.
That is because the macro DBO_EXTERN_TEMPLATES
will perform:
extern template class Wt::Dbo::ptr<C>;
extern template class Wt::Dbo::Dbo<C>;
Which generates the blocking default for the surrogateIdField
.
My apologies, I put it there as a force of habit.
I also would not advise that you go counter this flow, since it defines as early as possible that the class will be a Dbo class. So I do believe that declaring it in the headers is the safest way around this.
My location.h now looks like this:
#pragma once
#include <Wt/Dbo/Types.h>
namespace ContactForm {
namespace Dbo {
class Address;
class Location;
}
}
template<>
struct Wt::Dbo::dbo_traits<ContactForm::Dbo::Location> : public Wt::Dbo::dbo_default_traits
{
typedef ptr<ContactForm::Dbo::Address> IdType;
static IdType invalidId() {
return ptr<ContactForm::Dbo::Address>();
}
static const char *surrogateIdField() { return 0; }
};
namespace ContactForm {
namespace Dbo {
class Location
{
public:
Location() = default;
void setId(const Wt::Dbo::ptr<Address>& address);
template <class Action>
void persist(Action &a);
private:
Wt::Dbo::ptr<Address> addressPtr;
};
}
}
DBO_EXTERN_TEMPLATES(ContactForm::Dbo::Location)
If I move the dbo_traits
definition to the implementation, it will compile without the macro, but this leads to another issue. If I ask typeid(location.id()).name()
where location
is an instance of ContactForm::Dbo::Location
, it returns x
. Whereas it should return a (garbled) form of Wt::Dbo::ptr<ContactForm::Dbo::Address>
(N2Wt3Dbo3ptrIN11ContactForm3Dbo7AddressEEE
in my case). As such, I would not move this to the implementation.
On the other hand, if there are other Classes that need specialization, how would I go about that?
How can I guarantee the order of Compilation?
The order of compilation is not really something you should try to change (except for very large projects). Rather, try to observe the forward declaration pattern as much as possible. If you ensure that headers are very clean, and not link to each other, to avoid classes being declared earlier than you would expect them to be.
Can I have your compiler errors for reference
Here is the relevant part, but as I said above, I doubt there's an edge case, rather I included a macro out of habit you didn't.
/home/matthias/Documents/wt-support/forum/18360/location.cpp:9:12: error: specialization of ‘Wt::Dbo::dbo_traits<ContactForm::Dbo::Location>’ after instantiation
9 | struct dbo_traits<ContactForm::Dbo::Location> : public dbo_default_traits
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/matthias/Documents/wt-support/forum/18360/location.cpp:9:12: error: redefinition of ‘struct Wt::Dbo::dbo_traits<ContactForm::Dbo::Location>’
In file included from /home/matthias/Documents/wt/src/Wt/Dbo/Types.h:10,
from /home/matthias/Documents/wt/src/Wt/Dbo/Dbo.h:10,
from /home/matthias/Documents/wt-support/forum/18360/location.h:3,
from /home/matthias/Documents/wt-support/forum/18360/location.cpp:1:
As a small nitpick, can I suggest you use #include <Wt::Dbo::Types.h>
, which includes almost everything you could need in a Dbo class.
I hope this helps!
Best,
Matthias
RE: Relation Missmatch when following dbo::tutorial7 - Added by Christian Meyer almost 2 years ago
Thank you for your time Matthias
I finally had some sort of success ... but limited to a case that I don't want to use
I use static libraries to modularize specific parts of my program
One Part is the SessionModule, that takes care of the Database and changes to the db Structure etc.
The Next part is the ContactForm Module, with the Dbo Classes and Widgets related to that. As well as others that are self contained within their modules.
When I used the Dbo classes of ContactForm in an example function and included those files directly into the executable, the mapping works, even for the Module as a static library
as soon as the classes are not compiled into the executable itself, the error shows up at runtime.
whenever i put the template specialization in the header file, I can't get it to compile at all and have no idea as to how to solve that particular problem, unless putting the specialization in the cpp file.
But I would consider this case closed for now, Wt works, just not the way I use it =)
If you have an idea as to how to search for more information (keywords, directions to look into) I'd be grateful.
[SOLVED]: Relation Missmatch when following dbo::tutorial7 - Added by Christian Meyer almost 2 years ago
Solved this finally, even with my configuration of using it contained within a static library.
I moved the template specialization into a new header file: Location-traits.h
and include this where a Wt::Dbo::ptr<Location>
or weak_ptr
is defined. Namely in Address.h
Also included the DBO_EXTERN_TEMPLATES(ContactForm::Dbo::Location)
Makro in Location.h
again.
This seemed to have done the trick. It compiles, and the most important part: It works as expected now!
Thank you for the input and pointers