I am using Spring Security plugin in my grails app. I have extended Springs User class in my own generated Physician class. Now when I run app I am not getting physician table in database instead User class only has all properties defined in Physician Domain. I need to have separate table for Physician.
When I try to find all Users in User table with User.findAll() my output is,
[com.HospitalManagement.User : 1, com.HospitalManagement.User : 2, com.HospitalManagement.User : 3, com.HospitalManagement.User : 4, com.HospitalManagement.User : 5, com.HospitalManagement.User : 6, com.HospitalManagement.User : 7, com.HospitalManagement.User : 8, com.HospitalManagement.User : 9]
but I was expecting username and other physician properties values.
What could the problem be?
Domain Class is:
package com.HospitalManagement
class Physician extends User{
static constraints = {
}
String specilty;
String MobileNo;
String Physician_Address;
String clinicals;
}
By default, GORM uses a table-per-hierarchy model for domain classes with inheritance. All fields in the parent class and all fields in each subclass will be stored in a single table.
If you want to turn off this functionality, you can use the tablePerHierarchy mapping parameter. Setting this parameter to false will put the parent class fields in a common parent table, and put the fields for each subclass in their own table. This can make your queries slightly less efficient because the queries will will have joins, but if the inheritance tree is small, the difference should be negligible. Here's what it would look like with your domain class:
package com.HospitalManagement
class Physician extends User {
static constraints = {
}
String specilty;
String MobileNo;
String Physician_Address;
String clinicals;
static mapping = {
tablePerHierarchy false
}
}
See The grails documentation for more information:
Inheritance Strategies
Inheritance in GORM
If you want each subclass to have it's own table which contains all fields from the parent class and all fields from the subclass, then you can define the parent class as 'abstract' and that should prevent grails from making a separate table for it. Grails should only create tables for concrete domain classes, not abstract domain classes. [Source]
Your user class would look then look something like this:
abstract class User {
String username
String password
//etc...
}
This will build the tables correctly, though I'm not sure what effect it might have on Spring Security. If you see any Spring Security errors after making the User class abstract, I'd fall back to disabling table-per-hierarchy and dealing with the joins.
It sounds like you're trying to display the attributes of an object. Perhaps you just want to override toString() for your Physician class:
package com.HospitalManagement
class Physician extends User{
static constraints = {
}
String specilty;
String MobileNo;
String Physician_Address;
String clinicals;
String toString() {
"specilty: $specilty, MobileNo: $MobileNo, Physician_Address: $Physician_Address, clinicals: $clinicals"
}
}
or something like that, depending on how you want the output to be formatted.
Related
I'm using spring-data-mongodb at the moment so this question is primarily in context of MongoDB but I suspect my question applies to repository code in general.
Out of the box when using a MongoRepository<T, ID> interface (or any other Repository<T, ID> descendent) the entity type T is expected to be the document type (the type that defines the document schema).
As a result injecting such a repository into service component means this repository is leaking database schema information into the service tier (highly pseudo) :
class MyModel {
UUID id;
}
#Document
class MyDocument {
#Id
String id;
}
interface MyRepository extends MongoRepository<MyDocument, String> {
}
class MyService {
MyRepository repository;
MyModel getById(UUID id) {
var documentId = convert(id, ...);
var matchingDocument = repository.findById(documentId).orElse(...);
var model = convert(matchignDocument, ...);
return model;
}
}
Whilst ideally I'd want to do this :
class MyModel {
UUID id;
}
#Document
class MyDocument {
#Id
String id;
}
#Configuration
class MyMagicConversionConfig {
...
}
class MyDocumentToModelConverter implements Converter<MyModel, MyDocument> {
...
}
class MyModelToDocumentConverter implements Converter<MyDocument, MyModel> {
...
}
// Note that the model and the model's ID type are used in the repository declaration
interface MyRepository extends MongoRepository<MyModel, UUID> {
}
class MyService {
MyRepository repository;
MyModel getById(UUID id) {
// Repository now returns the model because it was converted upstream
// by the mongo persistence layer.
var matchingModel = repository.findById(documentId).orElse(...);
return matchingModel ;
}
}
Defining this conversion once seems significantly more practical than having to consistently do it throughout your service code so I suspect I'm just missing something.
But of course this requires some way to inform the mongo mapping layer to be aware of what conversion has to be applied to move between MyModel and MyDocument and to use the latter for it's actual source of mapping metadata (e.g. #Document, #Id, etc.).
I've been fiddling with custom converters but I just can't seem to make the MongoDB mapping component do the above.
My two questions are :
Is it currently possible to define custom converters or implement callbacks that allow me to define and implement this model <-> document conversion once and abstract it away from my service tier.
If not, what is the idiomatic way to approach cleaning this up such that the service layer can stay blissfully unaware of how or with what schema an entity is persisted? A lot of Spring Boot codebases appear to be fine with using the type that defines the database schema as their model but that seems supoptimal. Suggestions welcome!
Thanks!
I think you're blowing things a bit out of proportion. The service layer is not aware of the schema. It is aware of the types returned by the repository. How the properties of those are mapped onto the schema, depends on the object-document mapping. This, by default, uses the property name, as that's the most straightforward thing to do. That translation can either be customized using annotations on the document type or by registering a FieldNamingStrategy with Spring Data MongoDB.
Spring Data MongoDB's object-document mapping subsystem provides a lot of customization hooks that allows transforming arbitrary MongoDB documents into entities. The types which the repositories return are your domain objects that - again, only by default - are mapped onto a MongoDB document 1:1, simply because that's the most reasonable thing to do in the first place.
If really in doubt, you can manually implement repository methods individually that allow you to use the MongoTemplate API that allows you to explicitly define the type, the data should be projected into.
You can use something like MapStruct or write your own Singleton Mapper.
Then create default methods in your repository:
interface DogRepository extends MongoRepository<DogDocument, String> {
DogDocument findById(String id);
default DogModel dogById(String id) {
return DogMapper.INSTANCE.toModel(
findById(id)
);
}
}
How do I enable RestfulController to auto-map or even manually map the dynamic fields to domain classes implementing MongoEntity? I have a domain class as below:
class Company implements MongoEntity<Company> {
String id = UUID.randomUUID().toString()
String name
String email
String phone
}
And I have a RestfulController setup for CRUD operations as below
class CompanyController extends RestfulController<Company> {
#Transactional
def save(Company company) {
if(company.hasErrors()) {
respond company.errors
}
else {
company.insert(flush:true)
respond company, status: CREATED
}
}
}
When I POST a request with some additional JSON fields, how do I get them auto-mapped to gorm_dynamic_attributes ? Currently the company object does not return any information on the dynamic attributes. Another problem I am facing is that request.JSON is also null so I cannot manually map either. Any suggestions would be highly appreciated.
I'm pretty sure, that the problem is not in data binding of your controller, but rather in persisting of the domain class instance.
I would change the domain class like so:
import grails.gorm.annotation.Entity
#Entity
class Company {
String id
String name
String email
String phone
def beforeValidate() {
if( !id ) setId UUID.randomUUID().toString()
}
static mapping = {
id generator:'assigned'
}
}
to use the assigned generator. You could put your id generation either in the controller / service code, or leave it inside the domain class' beforeValidate. In the later case pay special attention to when the id shall be generated, as beforeValidate() is called pretty often. Also note, that inside beforeValidate() a setter must be called.
I tested the similar domain class of mine with save() and insert() and in both cases that works like charm.
Say I have a class structure as follows, it is pretty basic inheritance:
Manager extends Person {
private String name;
Manager() {
}
}
Clerk extends Person {
private String salary;
}
In spring Data if I store these in Mongo, is it possible to configure it to map the correct class when I do a getById. I assume i will have to store some class info?
What i dont want to do is the need to create seperate repository classes if i can avoid it, also i dont know what the object will be when i do a getById
If you are using spring-data-mongodb MongoRepository to write data in your database according to your entity model, a _class field will be added to document roots and to complex property types (see this section). This fields store the fully qualified name of the Java class and it allows disambiguation when mapping from MongoDb Document to Spring data model.
However, if you only use MongoRepository to read from your database, you need to tell Spring-data how to map your entities explicitly. You will need to Override Mapping with Explicit Converters.
PersonReadConverter.class
public class PersonReadConverter implements Converter<Document, Person> {
#Override
public Contact convert(Document source) {
if (source.get("attribute_specific_to_Clerk") != null) {
Clerk clerk = new Clerk();
//Set attributes using setters or defined constructor
return clerk;
}
else {
Manager manager = new Manager()
//Set attribute using setters or defined constructor
return manager;
}
}
}
Then, you have to Register Spring Converters with the MongoConverter.
You can find an example of my own at: Spring Data Mongo - How to map inherited POJO entities?
I'm trying to extend the RolePermissionSetting class with additional properties the thing is that this class is declared in abstract AbpRoleBase class, which is used across the framework is there a way to accomplish this without generating a different AbpPermissions table in the DB.
Here is the piece of code:
public abstract class AbpRoleBase : FullAuditedEntity<int>, IMayHaveTenant{
..
public virtual ICollection<RolePermissionSetting> Permissions { get; set; }
}
my Role class inherit from AbpRole(ZeroModule) which override AbpRoleBase, I'm afraid that if I replace that class with my own, EF will generate a new table in the model then I'll have two Role Tables.
EF will not generate a new table, but add a Discriminator column, which should be fine.
AbpRole implements, not overrides, AbpRoleBase. You can safely override Permissions property in Role.cs.
I am working on an mvc3 application and having some problems with getting validation to work as I want.
The application is using buddy classes for the models. (This is something I haven't used in the past and I am a little confused why they are used...anyway)
I want to add required fields to ensure data been submitted is correct. I have tried adding the required field to the buddy class.
When I submit the form no client-side validation takes place and the debugger steps into the entity frameworks generated code. Here is complains that the fields that contain null values are causing are invalid. If I step through all of those it finally gets to the controller where my if (ModelState.IsValid) is showing false.
I have client-side validation switched on.
Am I meant to be applying the data validation at the buddy class level or at the view model?
One other question is why use buddy classes? to me they seem to over complicate things.
Updated added an example of the buddy class
[MetadataType(typeof (CustomerMetaData))]
public partial class Customer
{
public string Priorty
{
get
{
var desc = (Priority) Priority;
return desc.ToString().Replace('_', ' ');
}
}
internal class CustomerMetaData
{
[Required]
[DisplayName("Priorty")]
public string Priorty { get; set; }
Buddy classes are metadata classes to put data annotation attributes when you are not in control of the original class i.e. can't edit it. Typical situation is when the class is generated by an ORM like Entity Framework.
//Can't edit this class
public partial class YourClass{
public string SomeField {get; set;}
}
//Add a partial class
[MetadataType(typeof(YourClassMetadata))]
public partial class YourClass{
}
//And a metadata class
public class YourClassMetadata
{
[Required(ErrorMessage = "Some Field is required")]
public string SomeField {get; set;}
}
are you sure that you have [MetadataType(typeof(YourClassMetadata))]?
More about buddy classes here and here
You would typically use a buddy class when it isn't possible to add meta data to an entity class such as when a model is automatically generated by an ORM tool. In this case any meta data you had applied would be lost.
Therefore, your original (automatically generated) class would be defined as a partial class:
public partial class Customer
{
public string Priority { get; set; }
}
And then you would generate your buddy classes to add the meta data.
[MetadataType(typeof(CustomerMetaData))]
public partial class Customer
{
}
internal class CustomerMetaData
{
[Required]
public string Priority { get; set; }
}
You would then pass the Customer class to the view where the Priority would be set.
In your case i'm not sure if you only have one partial class or two (as the other is not shown but please provide if there is). I'm interested to know how you obtain the priority information from the customer as i'm wondering if this is an issue with how you use ModelState.IsValid? The reason I ask is that no set accessor is declared on the Priority property so i'm wondering how this is set from the view in order to report that it is not valid?
You would also use a buddy class when it isn't possible to add meta data to an entity class such as when a model is automatically generated by an WCF Data Contract.