Reputation: 19928
Lets say I have the following Spring Data MongoDB interface:
public interface AccountRepository extends MongoRepository<Account, String> {}
Now the object Account is displayed below:
public class Account {
@Id
private String id;
private Authentication authentication; //this is from the interface org.springframework.security.core.Authentication. Here I'm using org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken which implements the interface
When I call the below, Jackson will not save the account object as I do not have a model for Authentication that it can use to map the inner Authentication object. Furthermore, Jackson requires that there be a public empty constructor of Authentication() and the Spring Authentication class does not have such a constructor:
@Autowired
AccountRepository accountRepo;
accountRepo.save(Account);
I had to change the account model to the following to save Authentication as an Object:
public class Account {
@Id
private String id;
private Object authentication;
However, when I try to retrieve the authentication object and recast it to an Authentication object, I get this error:
{
"timestamp": 1438441062525,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.data.mapping.model.MappingException",
"message": "No property aPrincipal found on entity class com.futureprocessing.spring.infrastructure.AuthenticatedExternalWebService to bind constructor parameter to!",
"path": "/account"
}
I can always save the pieces of Authentication separately in my account object (Principal, Credentials and Grant Authorities) but then I will have to do a constructor to recreate it if it is needed.
Is there no easier way to just save an Authentication object as an Authentication object?
Upvotes: 1
Views: 1181
Reputation: 83051
Due to the change in the object model, and the previous version using stronger types, the already existing documents in the database to not contain any type information. This causes Spring Data MongoDB understandably fail to derive that you want to read an embedded Authentication
when all it can reason about is a property of type Object
.
The issue here is that your database doesn't contain any type information as you stored documents derived from the class model when the authentication
property still was an Authentication
. If at storage time, the value of the property is of the exact same type as the declared property, Spring Data MongoDB doesn't write any type information to avoid additional overhead.
If you persist an Account
with the authentication
property declared as object but containing an Authentication
object, Spring Data MongoDB will add an _class
property to the embedded document capturing the type information, so that when reading the document the mapper knows which type to instantiate.
Without that additional property in the document and the authentication
property in the class of type Object
, how would the mapper know you'd want to instantiate an Authentication
object when reading a document?
The easiest way to fix this is by manually issuing an update to the database, adding the missing type information. By default we're using fully qualified class names so that something like this should do the trick:
Update setTypeInformation = Update.update("authentication._class", Authentication.class.getName());
template.updateMulti(new Query(), setTypeInformation, Account.class);
The Update
statement adds the _class
property to the embedded authentication
document and sets it to the fully qualified name of Authentication
. The Update
is the executed against all Accounts
due to the empty Query
which matches all existing documents.
More information on this can be found in the corresponding section of the reference documentation.
Upvotes: 1