I have a "strange" behavior with list properties of any kind of entity.
When I try to set a non empty list to a retrieved entity that has a list value with null (because is not fetched) the entity doesn't "reflect" the new non-empty values that I set.
Steps
Retrieve any entity (ex. user) from database in an ejb with criteria.
The retrieved entity has a OneToMany attribute (roles) by default is lazy that's why if I don't do a fetch (I don't) the list is null
Then try to fill the role list with a NON-EMPTY list
Here the list that I set has values but the user list has a null in the roles list attribute even though I set it in the step 3
I don't know why this is happening, the only way I could fix this is cloning (using SerializationUtils from Apache commons.lang3) the user entity (see the steps) and with this work, but I don't know why.
Retrieve any entity (ex. user) from database in an ejb with criteria.
Clone retrieved entity and assign to new one User clonedUser = SerializationUtils.clone(originalRetrivedUser);
Then try to fill the role list with a NON-EMPTY list to the clonedUser
The list of clonedUser has the correct values
Why do I need to clone? Why can't I set a list to the entity if is not cloned?
I'm using Apache TomEE 1.6.0-SNAPSHOT (with the openjpa version embedded)
***** ENTITIES *****
#Entity
public class User implements Serializable{
#Id
private int id;
private String userName;
private String password;
#OneToMany(mappedBy = "user")
private List<Role> roles;
//getters and setters..
}
#Entity
public class Role implements Serializable{
#Id
private int id;
private String roleName;
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
//getters and setters..
}
**** EJB INTERFACE LOCAL ****
#Local
public interface MyEJBLocal{
public User getUserWithRoles();
}
**** EJB CLASS ****
#Stateless
public class MyEJB implements MyEJBLocal{
#PersistenceContext(unitName ="ANY_NAME")
private EntityManager em;
#Override
public User getUserWithRoles(){
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.where(cb.equal(root.get(User_.userName),"john"));
User userJohn = em.createQuery(cq).getSingleResult(); // if I want this work I have to do User userJohn = SerializationUtils.clone(em.createQuery(cq).getSingleResult()); [1*]
//I will create a list of role just for try to set any role the userJohn
List<Role> roleList = new ArrayList<Role>(2);
roleList.add(new Role(1,'ADMIN'); //creating and adding role 1
roleList.add(new Role(2,'DEVELOPER');//creating and adding role 2
//setting the list of roles created to the user, as you can see the list has 2 values but the value roles of userJohn always is set to null, my setters and getters are correct
userJohn.setRoles(roleList);
return userJohn;
}
}
*** MANAGED BEAN ****
#Named
public class MyBean implements Serializable{
#EJB
private MyEJBLocal ejb;
public void anyMethod(){
User user = ejb.getUserWithRoles();
user.getRoles(); //<---- HERE THE LIST IS ALWAYS NULL but if I use clone in the ejb method (see 1*) I can get the corrected values.
}
}
I know I can get the values by fetching but I'm trying not to do it.
In the previous example I tried to set the list in the ejb, but if I remove and then put the list in the managed bean I get the same behavior
I just want want to retrieve an entity and add some info but I don't want to persist that new info.
I'm trying to use the detach method but nothing changes.
I don't want to use #Transient because sometimes I need to persist the data, the idea is easy, I want to retrieve an entity then in the managedbean I want to add a new list but no matter what I set to the list (roles) of the entity (user) the list on the entity always set to null like if the list in the entity is protected in some way.
Related
In Spring Data Elasticsearch is there a way of detecting that the mapping on the index does not match the mapping created from the Entity object?
i.e.
I allowed Spring Data Elasticsearch to create the mapping originally from the entity model annotated with #Document and #Field
At a later point I add a new field to the model, but do not update the index mapping, this new field then won't be correctly configured until I re-index to the new mapping
So is there a way to detect such discrepancies so I will know which indexes need to have their mappings re-created, and documents re-indexed?
This is an interesting question, but the answer is not too complicated.
Let's assume we have a Foo entity class to be stored in an foo index and a FooRepository that uses this class. On
application startup, when the index does not exist, it will be created with the mapping derived from the entity.
In order to detect changes in the maping derived from the class and the one stored in Elasticsearch you can use an
approach like this:
#Component
public class FooMappingValidator {
private static final Logger LOGGER = LoggerFactory.getLogger(FooMappingValidator.class);
private final ObjectMapper objectMapper = new ObjectMapper();
private final ElasticsearchOperations operations;
public FooMappingValidator(ElasticsearchOperations operations) {
this.operations = operations;
}
#Autowired
public void checkFooMapping() {
var indexOperations = operations.indexOps(Foo.class);
if (indexOperations.exists()) {
LOGGER.info("checking if mapping for Foo changed");
var mappingFromEntity = indexOperations.createMapping();
var mappingFromEntityNode = objectMapper.valueToTree(mappingFromEntity);
var mappingFromIndexNode = objectMapper.valueToTree(indexOperations.getMapping());
if (!mappingFromEntityNode.equals(mappingFromIndexNode)) {
LOGGER.info("mapping for class Foo changed!");
indexOperations.putMapping(mappingFromEntity);
}
}
}
}
This is a Spring component that has ElasticsearchOperations injected and that has a method that is annotated with
#Autowired. An autowired method is executed after all the dependencies have been injected in the beans. This means
it runs before the normal application logic is started.
In this method we first get an IndexOperations instance for our entity class. Next we check if the index exists,
if it doesn't, we do not need to check.
In the next step we get the current mapping from the entity and convert it to a JsonNode and do the same with the
mapping we retrieve from Elasticsearch. We use JsonNodes here because the have an equals() method that
does the comparison we need.
If we detect that the both mappings are different, we write the new one to the index with the putMapping() method.
NOTE:
This only works when new properties are added to the entity, as existing mappings cannot be changed in Elasticsearch,
there you'd need reindexing.
I'm using MVC 5 and EF 6 (Datafirst),using msssql management studio.
I created a new mvc project, which came up with built database (AspNetUsers etc)
I also created a new table called UserDetails, which it purpose to contain more details about the user by it's Id (so I created a link between AspNetUsers id column to UserDetails UserId column)
therefore I added the following code
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaim(new Claim("FirstName", FirstName.ToString()));
return userIdentity;
}
//Extended Propeties
public string FirstName { get; set; }
}
But of course it's not working, I looked over the internet for over 4 hours now, can someone please guide me ? I'm new to MVC, everything seems to be complicated much.
I also have the following static method :
public static class IdentityExtensions
{
public static string GetFirstName(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("FirstName");
// Test for null to avoid issues during local testing
return (claim != null) ? claim.Value : string.Empty;
}
}
in order to get it in the view and display it..
my goal is to display data from another table (UserDetails) in the view based on connection of 1-1 from AspNetUsers (UserDetails.UserId == AspNetUsers.Id)
All you have to do is extend the IdentityUser class, then add your custom properties like FirstName etc.. then since you are using EntityFramework Database first you need to enable the migrations with this command in your package manager console enable-migrations, then add an initial migration like add-migration initialMigration, after that update the database using migrations with this command update-database, the AspNetUsers table in your database will now have the new columns you added. Use migration to keep your database in sync with your models
if there is correct connection between both table you can use Eagerly Loading to get the details of one entity from another.
I have an application where we have elaborate authorization process. However in a method that returns IQueryable, we do not have control on which entities in child collection will be returned.
Here is an example:
public class Parent{
public int Id {get; set;}
public string Name {get; set;}
public virtual ICollection<Child> Children {get; set;}
}
public class Child {
.... details of child class
}
public MyContext : DbContext{
public DbSet<Parent> Parents;
public DbSet<Child> Children;
}
Now we have different level permissions on the parent and child items and the user with read permission on parent may not have same permission on all child items in the parent. However I have an OData Web API controller written on top of this model which has a Get method on the Parent class with IQueryable as return type and EnableQuery attribute.
public class ParentController : ODataController{
private readonly MyContext _db = new MyContext();
[EnableQuery]
public IQueryable<Parent> Get{
return _db.Parents;
}
.... other methods
}
I have no control on what query the end user will send and if user asks for children, the DBSet will return all the children irrespective of the access permission.
Is there any way I can filter the Children property of the Parent class?
The end user can only use a $expand query to get all children. Therefore he can only query again associated entities. However sometimes you need to filter more data. Then you have to options:
(The complex one) Create a View or Stored Procedure with tvf. See this answer for more details.
(The easy one) Use a dynamic filter for soft delete flags. You can use this if you just want to filter data that has a flag like IsDeleted. Than you can include this extension and enable it like this:
modelBuilder.Filter("IsDeleted", (ISoftDelete d) => d.IsDeleted, false);
This will only show the datasets that do not have the IsDeleted flag set to true.
The #Query on the property retrieves the values only if I retrieve the entity from the DB.
#NodeEntity
public class Team
{
#GraphId
private Long nodeId;
#RelatedTo (type = "PREVIOUSLY_KNOWN_AS")
private Team previouslyKnownAs;
#Query ("START t=node({self}) MATCH t-[:PREVIOUSLY_KNOWN_AS]-other RETURN other")
private Iterable<Team> aliases;
}
The below test works only if I uncomment the line to read it explicitly from the db. Why is it necessary? I see the query being run after the save(t) but the alias field is null if I doesn't read it from DB by uncommenting the line
#Test
public void alias()
{
Team t = new Team();
t.setName("Alpharetta One");
Team prev = new Team();
prev.setName("Previous Name");
teamRepo.save(prev);
t.setPreviouslyKnownAs(prev);
teamRepo.save(t);
//t = teamRepo.findOne(t.getNodeId());//only works if I uncomment
assertNotNull(t.getAliases());
}
Try
t=teamRepo.save(t);
I dont think that the save operation will update the POJO which you give to it, while the returned Object should be a managed enttiy.
The key lies in the reference documentation
The #Query annotation leverages the delegation infrastructure supported by Spring Data Neo4j.It provides dynamic fields which, when accessed, return the values selected by the provided query language expression.
Since it is a dynamic field, the value isnt instanciated but instead fetched from the DB every time the get method is called. To do this, a proxy object has to be used. However there is no way for SDN to change your t Object reference to the proxy object, and thats why its not working, if you are not using the entity returned by save().
I am sure this question has been asked before, so I apologize in advance, but am not sure of the correct keywords to include in my searches...
I am having trouble understanding the proper pattern for updating (or even inserting) an object when one of its properties is a collection of other properties in a disconnected environment (like a website). My issue has to do with the idea that a web application is only returning a collection of id's as opposed to the full object. I think the best way to explain this is with code snippets.
Given the following objects
Public Class User
Public Property UserId As Integer
Public Property Username As String
Public Property Roles As ICollection(Of Role)
End Class
Public Class Role
Public Property RoleId As Integer
Public Property RoleName As String
Public Property Users As ICollection(OF User)
End Class
Public Class EFDbContext
Inherits Entity.DbContext
Public Property Users As Entity.DbSet(Of User)
Public Property Roles As Entity.DbSet(Of Role)
End Class
A database is created with 3 tables - Users, Roles, and RoleUsers.
I know I can easily do the following
Dim db = New EFDbContext()
Dim r1 = New Role() With { .RoleName = "User" }
Dim r2 = New Role() With { .RoleName = "Admin" }
db.Roles.Add(r1)
db.Roles.Add(r2)
Dim u1 = New User() With { .UserName = "test1", .Roles = New List(Of Role) }
u1.Roles.Add(r1)
db.Users.Add(u1)
db.SaveChanges()
And it will save both new roles to the database (giving them RoleId values of 1 and 2 respectively), a new user (giving it a UserId value of 1) and a new Role-User entry with RoleId 1 and UserId 1.
However, when dealing with a disconnected scenario like a website, most people would have a View Model to represent the input from the user which then gets mapped back to the Entities. In addition, for values representing the Roles, the data coming back would most likely only contain the unique key representing the Role. For example,
Public Class UpdatedUserViewModel
Public Property UserId As Integer
Public Property Username As String
Public Property RoleIds As ICollection(Of Integer)
End Class
...
...
Dim userEntity = db.Users.Find(user.Values.UserId)
AutoMapper.Mapper.Map(userValues, userEntity)
So while the userEntity.Roles collection may contain a single item, the mapper probably just added the entry with something like
ForMember(Function(u) u.Roles, Sub(m) m.MapFrom(Function(su) su.RoleIds.Select(Function(r) New Role() With {.RoleId = r})))
And now we come to the problem, when the SaveChanges() method is called, EF throws a Validation error because the .RoleName property is Nothing.
How does this situation get handled? Are we supposed to manually loop through the Roles and fetch each one from the database? Can we not use mapping tools? Do I give bogus values for the "missing" properties and then loop through and mark them as Unchanged?
I know this was long but I thought the walk-throughs would be helpful...
Thanks.
You can use this algorithm
Start with the root entities.
For each root entity, e.g. a of type A, set a's properties except for navigation properties (at least all the mandatory ones (non-nullables))
Add the As to the context.
Next prepare child entities (entities that must have exactly 1 A) e.g. b of type B.
Set b's properties (except navigations, at least all non-nullables).
For each b, add b to its a (e.g. a.Children.Add(b)).
Continue with child entities of above
...
Save and apply changes
If you have an entity with a non-nullable navigation that already exists in DB and has not yet been accessed via context, you can set the relationship by ID (assuming you've mapped the FK to a property in the model) instead of setting the entity itself.
If your IDs are not store generated, make sure you set them too. If they are, make sure they are defined as store generated in EDMX.
If you have FKs in the DB, make sure the EDMX is aware of them so that the inserts will happen in the correct order (or if using Oracle you can try using deferred constraints instead if you want).