Recursive method, load children. Java - java

I have an entity that has an attribute "father" is a reference to his father. (Like this in the database)
Now I need to create a tree in view, and I have to carry the children recursively.
I have made the method below, but I doubled the children. And not further if the (optimal) correct way to do this process.
Can somebody help me.
Thank you.
#Transactional(readOnly = true)
public Page<CategoriaDTO> findAll(Pageable pageable) {
log.debug("Request to get all Categorias");
Page<Categoria> result = categoriaRepository.findByPadreIsNull(pageable);
List<CategoriaDTO> categoriaDtos = new ArrayList<>();
for (Categoria categoriaAux : result) {
CategoriaDTO categoriaDto = categoriaMapper.categoriaToCategoriaDTO(categoriaAux);
categoriaDto.setHijos(categoriaMapper.categoriasToCategoriaDTOs(categoriaRepository.findByPadre(categoriaAux)));
hijos(categoriaDto.getHijos(),categoriaDto.getId());
categoriaDtos.add(categoriaDto);
}
return new PageImpl<CategoriaDTO>(categoriaDtos);
}
private void hijos(List<CategoriaDTO> hijos,Long padreId){
Categoria categoriaPadre = categoriaRepository.findOne(padreId);
if(! CollectionUtils.isEmpty(hijos)){
for (CategoriaDTO hijo : hijos) {
hijo.setHijos(categoriaMapper.categoriasToCategoriaDTOs(categoriaRepository.findByPadre(categoriaPadre)));
hijos(hijo.getHijos(),hijo.getId());
}
}
}

As I understand, in your entity you have a reference to the same entity.
So you could start the leaf and go up in the tree. Call the function recursively until the Entity has null parent(that means you reached the root). You also add the Entities to a list.
LinkedList<Category> list = new LinkedList<>();
public void getChildren(Category category) {
list.add(category);
if(category.getParent() == null)
return;
getChildren(category.getParent());
}
This is just an example, post your Entity for more details

Related

Mapping multiple DTO to entities - nested exception

I'm trying to map DTOs to entities. I created a service that only takes care of mapping objects - ObjectMapper. DTO objects have relationships with each other. When I map a single object, for example when I create User, Group, Note, everything works. But when I want to use a method that returns a Note with a specific ID - /notes/{id}, I get the following error.
Handler dispatch failed; nested exception is java.langStackOverflowError] with root cause
To get specific Note, I need to use this mapping method that also cause this error. As u can see, I have to also convert Group and Tags.
//Note
public NoteDTO NoteEntityToDtoGet(Note note) {
NoteDTO noteDTO = new NoteDTO();
noteDTO.setId(note.getId());
noteDTO.setTitle(note.getTitle());
noteDTO.setDescription(note.getDescription());
noteDTO.setGroup(GroupEntityToDtoGet(note.getGroup()));
noteDTO.setTags(TagConvertSet(note.getTags()));
return noteDTO;
}
When I don't have relationships defined as another DTO in the DTO class, but as an entity, everything works, since I don't have to convert the DTO to an entity.
Do you know where I'm making a mistake when mapping? Am I making a mistake in mapping multiple objects at once?
ObjectMapper
#Service
public class ObjectMapper {
//User
public UserDTO UserEntityToDtoGet(User user) {
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setName(user.getName());
userDTO.setEmail(user.getEmail());
userDTO.setGroup(user.getGroups());
return userDTO;
}
private UserCreationDTO UserEntityToDtoCreate(User user) {
UserCreationDTO userCreationDTO = new UserCreationDTO();
userCreationDTO.setName(user.getName());
userCreationDTO.setEmail(user.getEmail());
return userCreationDTO;
}
private User UserDtoToEntityCreate(UserCreationDTO userCreationDTO) {
User user = new User();
user.setName(userCreationDTO.getName());
user.setEmail(userCreationDTO.getEmail());
return user;
}
//Group
public GroupDTO GroupEntityToDtoGet(Group group) {
GroupDTO groupDTO = new GroupDTO();
groupDTO.setId(group.getId());
groupDTO.setName(group.getName());
groupDTO.setUser(UserEntityToDtoGet(group.getUser()));
groupDTO.setNotes(NoteConvertList(group.getNotes()));
groupDTO.setTags(TagConvertSet(group.getTags()));
return groupDTO;
}
public GroupCreationDTO GroupEntityToDtoCreate(Group group) {
GroupCreationDTO groupCreationDTO = new GroupCreationDTO();
groupCreationDTO.setName(group.getName());
groupCreationDTO.setUser(UserEntityToDtoGet(group.getUser()));
groupCreationDTO.setTags(TagConvertSet(group.getTags()));
return groupCreationDTO;
}
public Group GroupDtoToEntityCreate(GroupCreationDTO groupCreationDTO) {
Group group = new Group();
group.setName(groupCreationDTO.getName());
return group;
}
//Note
public NoteDTO NoteEntityToDtoGet(Note note) {
NoteDTO noteDTO = new NoteDTO();
noteDTO.setId(note.getId());
noteDTO.setTitle(note.getTitle());
noteDTO.setDescription(note.getDescription());
noteDTO.setGroup(GroupEntityToDtoGet(note.getGroup()));
noteDTO.setTags(TagConvertSet(note.getTags()));
return noteDTO;
}
public Note NoteDtoToEntityCreate(NoteCreationDTO noteCreationDTO) {
Note note = new Note();
note.setTitle(noteCreationDTO.getTitle());
note.setDescription(noteCreationDTO.getDescription());
return note;
}
public NoteCreationDTO NoteEntityToDtoCreate(Note note) {
NoteCreationDTO noteCreationDTO = new NoteCreationDTO();
noteCreationDTO.setTitle(note.getTitle());
noteCreationDTO.setDescription(note.getDescription());
return noteCreationDTO;
}
public List<NoteDTO> NoteConvertList(List<Note> note) {
return note.stream()
.map(this::NoteEntityToDtoGet)
.collect(Collectors.toList());
}
//Tag
public TagDTO TagEntityToDtoGet(Tag tag) {
TagDTO tagDTO = new TagDTO();
tagDTO.setId(tag.getId());
tagDTO.setName(tag.getName());
tagDTO.setNotes(tag.getNotes());
tagDTO.setGroups(tag.getGroups());
return tagDTO;
}
public TagCreationDTO TagEntityToDtoCreate(Tag tag) {
TagCreationDTO tagCreationDTO = new TagCreationDTO();
tagCreationDTO.setId(tag.getId());
tagCreationDTO.setName(tag.getName());
tagCreationDTO.setNotes(tag.getNotes());
return tagCreationDTO;
}
public Set<TagDTO> TagConvertSet(Set<Tag> groups) {
return groups.stream()
.map(this::TagEntityToDtoGet)
.collect(Collectors.toSet());
}
}
You get StackOverFlowError because you end up with infinite recursive methods call and your application creates infinite amount of objects, so you just run out of memory:
1) your NoteEntityToDtoGet method gets Note's group and calls GroupEntityToDtoGet method on the Group object;
2) in GroupEntityToDtoGet method you get all Group's notes and call NoteConvertList method on them, which calls NoteEntityToDtoGet on each of the 'Note'
3) step 1 again...
... the same cycle goes over and over without a stop until your stack memory, you know, overflows :)
So you should decide do your DTO classes really need to hold references to other entity collections.

Recursive TreeView creation with the data provided in this question?

I have a Treeview TreeView<MyType> which I'd like to fill recursively from a MyType root object. The structure of the class MyType is the following:
public class MyType {
private Set<MyType> children = new HashSet<>();
public Set<MyType> getChildren() {
return children;
}
public void setChildren(Set<MyType> children) {
this.children = children;
}
}
So as you can see, the MyType root/parent has children of the same type, and those children can also have children from the same type. In practice, the depth between the root and its furthermost inheritant is no greater than 1000 level.
I want to fill the Treeview TreeView<MyType> recursively with tree items TreeItem<MyType>in the same tree structure as the data is stored in the MyType root file.
This is what I've tried so far but it's not working:
void buildTree(MyType parent, TreeItem<MyType> result) {
for (MyType child : parent.getChildren()) {
if (child.getChildren() == null || child.getChildren().isEmpty()) {
result.getChildren().add(new TreeItem<MyType>(child));
}
else {
TreeItem<MyType> tmp = new TreeItem<>(child);
buildTree(child, tmp);
}
}
}
Is it possible to make the filling work with the data structure provided?
It's more convenient to
A. Return TreeItems instead of passing both MyType and TreeItem to the recursive method calls.
B. Treat leafs as terminal cases instead of handling terminal cases at parents of leafs
This allows you to write the following code:
private TreeItem<MyType> buildSubtree(MyType root) {
TreeItem<MyType> result = new TreeItem<>(root);
if (root.getChildren() != null) {
for (MyType child : root.getChildren()) {
result.getChildren().add(buildSubtree(child));
}
}
return result;
}
Ok, I just figured it out. This is working as expected.
void buildTree(MyType parent, TreeItem<MyType> result) {
for (MyType child : parent.getChildren()) {
if (child.getChildren() == null || child.getChildren().isEmpty()) {
result.getChildren().add(new TreeItem<MyType>(child));
}
else {
TreeItem<MyType> tmp = new TreeItem<>(child);
buildTree(child, tmp);
result.getChildren().add(tmp);
}
}
}

getLinks method returns deleted Entities, how to prevent it?

Below is my code to get the list of Entity's linked. It works but the problem is that even the deleted Entity is returned, although the Entity is already emptied out and it is the only property set. Is there a way to not return the deleted entities at all? Or is there a way to filter it out?
EntityId idOfEntity = txn.toEntityId(entityId);
Entity txnEntity = txn.getEntity(idOfEntity);
EntityIterable result = txnEntity.getLinks(Arrays.asList(new String[] {linkName}));
for (Entity entity : result) {
}
When you delete an entity, it's your responsibility to check if there are incoming links to the deleted entity. Otherwise so called "phantom links" can appear. You can set -Dexodus.entityStore.debug.searchForIncomingLinksOnDelete=true (PersistentEntityStoreConfig#setDebugSearchForIncomingLinksOnDelete(true)) to debug deletion in your application. With this setting, Xodus searches for incoming links to each deleted entity and throws EntityStoreException if it finds. The setting should not be used in production environment as it significantly slows down entity deletion performance.
Here's the complete code that I have come up with:
#Override
public boolean deleteEntities(String instance, String namespace, final String entityType) {
final boolean[] success = {false};
final PersistentEntityStore entityStore = manager.getPersistentEntityStore(xodusRoot, instance);
try {
entityStore.executeInTransaction(
new StoreTransactionalExecutable() {
#Override
public void execute(#NotNull final StoreTransaction txn) {
EntityIterable result = null;
if (namespace != null && !namespace.isEmpty()) {
result =
txn.findWithProp(entityType, namespaceProperty)
.intersect(txn.find(entityType, namespaceProperty, namespace));
} else {
result =
txn.getAll(entityType).minus(txn.findWithProp(entityType, namespaceProperty));
}
final boolean[] hasError = {false};
for (Entity entity : result) {
entity.getLinkNames().forEach(linkName -> {
Entity linked = entity.getLink(linkName);
entity.deleteLink(linkName, linked);
});
// TODO: This is a performance issue
final List<String> allLinkNames = ((PersistentEntityStoreImpl) entityStore).getAllLinkNames((PersistentStoreTransaction) entityStore.getCurrentTransaction());
for (final String entityType : txn.getEntityTypes()) {
for (final String linkName : allLinkNames) {
for (final Entity referrer : txn.findLinks(entityType, entity, linkName)) {
referrer.deleteLink(linkName, entity);
}
}
}
if (!entity.delete()) {
hasError[0] = true;
}
}
success[0] = !hasError[0];
}
});
} finally {
// entityStore.close();
}
return success[0];
}

How to delete Entity Type with Xodus?

Here's my code to delete all entities for a given type:
#Override
public boolean deleteEntities(String instance, final String storeName) {
final boolean[] success = {false};
final PersistentEntityStore entityStore = manager.getPersistentEntityStore(xodusRoot, instance);
try {
entityStore.executeInTransaction(new StoreTransactionalExecutable() {
#Override
public void execute(#NotNull final StoreTransaction txn) {
EntityIterable result = txn.getAll(storeName);
final boolean[] hasError = {false};
for(Entity entity : result) {
if(!entity.delete()) {
hasError[0] = true;
}
}
success[0] = !hasError[0];
}
});
} finally {
////entityStore.close();
}
return success[0];
}
Question:
Is this the right approach to delete all existing entities for a given entity type?
When this method is executed, all entities are indeed removed but the Enity type is sitll there, how to properly delete a enity type?
There is PersistentEntityStore#renameEntityType to rename entity type as part of a public api. To delete entity type at all you can use PersistentEntityStoreImpl#deleteEntityType. It's not a part of PersistentEntityStore api but method is public and you can use it.
Also when you deleting entity type do not forget that you also need to clear all links points to entities of this type.

ArrayList of custom Java objects over BlazeDS into AS3.0

Right away i just try to explain my problem:
Using BlazeDS i got the following Javaclasses:
DatabaseService.java:
public class Database {
private Category helpCat = null;
private Category root = new Category(1, "root", null, null);
private List<Article> database;
public Database()
{
// ------------ tree -----------------------------------------------------------------------
List<Category> level_one = new ArrayList<Category>();
List<Category> level_two_computer = new ArrayList<Category>();
List<Category> level_tree_hardware = new ArrayList<Category>();
// Level 1
Category buecher = new Category(2, "buecher", root, null);
Category computer = new Category(3, "computer", root, level_two_computer);
level_one.add(buecher);
level_one.add(computer);
//Level 2
Category hardware = new Category(4, "hardware", computer, level_tree_hardware);
Category software = new Category(5, "software", computer, null);
level_two_computer.add(hardware);
level_two_computer.add(software);
//Level 3
Category graphic = new Category(6, "graphic", hardware, null);
Category sound = new Category(7, "sound", hardware, null);
level_tree_hardware.add(graphic);
level_tree_hardware.add(sound);
// Level 0
root.addChilds(level_one);
// ------ tree end ----------------------------------------------------------------------------
database = new ArrayList<Article>();
try {
add(new Book("Per Anhalter durch die Galaxis", "42", Articletype.BOOK, 795, "Per Anhalter durch die Galaxiss", "Douglas Adams", "Heyne Verlag", "Taschenbuch", "3453146972"), buecher);
add(new Book("Harry Potter und der Stein der Weisen", "descriptionShort", Articletype.BOOK, 1299, "Harry Potter und der Stein der Weisen", "Joanne K. Rowling", "Carlsen Verlag GmbH", "gebunden", "3551551677"), buecher);
add(new Book("Harry Potter und die Kammer des Schreckens", "descriptionShort", Articletype.BOOK, 1499, "Harry Potter und die Kammer des Schreckens", "Joanne K. Rowling", "Carlsen Verlag GmbH", "gebunden", "3551551677"), buecher);
add(new Hardware("nVidia GeForce 8400GS", "Graphikkarte", Articletype.HARDWARE, 2665, "512 GDDR5 Speicher, DVI, 1 GPU", "MSI", "neu"), graphic);
add(new AKW("Biblis C", "Druckwasserreaktor, Preis auf Anfrage, Nur Selbstabholer", Articletype.AKW, -1, "Biblis", 0, 2525, "Siemens", 1, 2012), software);
} catch (Exception e) {
e.printStackTrace();
}
}
public List<Category> getCategories(String node) {
if(node.equalsIgnoreCase("root"))
return root.getChildren();
Category baum = null;
baum = get_node_by_name(root, node);
return baum.getChildren();
}
private Category get_node_by_name(Category localroot, String lookfor)
{
helpCat = null;
if(localroot.getChildren() != null)
{
for (int i = 0; i < localroot.getChildren().size(); ++i)
{
if(!(localroot.getChild(i).getName().equals(lookfor)))
{
get_node_by_name(localroot.getChild(i), lookfor);
}
else
{
helpCat = localroot.getChild(i);
helpCat.setParent(null);
}
}
}
return helpCat;
}
public List<Article> search(int artID, String name, Category categorie){
List<Article> ergebnis = new ArrayList<Article>();
if (artID >= 0)
{
for(int i = 0; i< database.size(); ++i){
if(database.get(i).getID() == artID)
{
ergebnis.add(database.get(i));
return ergebnis;
}
}
}
if (name != null){
for(int i = 0; i<database.size(); ++i){
if (database.get(i).getName().equalsIgnoreCase(name))
ergebnis.add(database.get(i));
}
return ergebnis;
}
if (categorie != null){
{
ergebnis.addAll(categorie.getArticles());
}
return ergebnis;
}
return database;
}
public Article add(Article newArticle, Category cat) throws Exception
{
newArticle.addCategory(cat);
if(newArticle.getID() != 0)
{
throw new Exception("Die Artikel ID wird vom DBS festgelegt!");
}
if (database.isEmpty())
{
newArticle.setID(0);
}
else
{
newArticle.setID(database.get(database.size() - 1).getID()+1);
}
database.add(newArticle);
return newArticle;
}
}
And the Category Class:
public class Category {
private int idCat;
private String nameTEST;
private Category parent = null;
private List<Article> articles = new ArrayList<Article>();
private List<Category> children = new ArrayList<Category>();
public Category(int _id, String _name, Category _parent, List<Category> _children)
{
if(_id > 0)
idCat = _id;
if(_name != null)
nameTEST = _name;
if(_parent != null)
parent = _parent;
if(_children != null)
children = _children;
}
public String toString()
{
return nameTEST;
}
void addArticle(Article article){
articles.add(article);
}
public List<Article> getAllArticles(){
List<Article> ergebnis = this.getArticles();
for (int i = 0; i<children.size();++i){
ergebnis.addAll(children.get(i).getAllArticles());
}
return ergebnis;
}
public void setID(int iD) {
idCat = iD;
}
public int getID() {
return idCat;
}
public void setName(String name) {
this.nameTEST = name;
}
public String getName() {
return nameTEST;
}
/**
* #param parent the parent to set
*/
public void setParent(Category parent)
{
this.parent = parent;
}
/**
* #return the articles
*/
public List<Article> getArticles()
{
return articles;
}
public void addChilds(List<Category> _next)
{
for (int i = 0; i < _next.size(); ++i)
{
children.add(_next.get(i));
}
}
public void addChild(Category one_next)
{
children.add(one_next);
}
public Category getChild(int index)
{
return children.get(index);
}
public void removeChild(Article article){
articles.remove(article);
}
public List<Category> getChildren()
{
return this.children;
}
}
also there are of course classes for articles and so on, but thats not important at that point.
the counterpart in flex looks like this:
Category.as
[RemoteClass(alias="PACKAGE.Category")]
public class Category
{
private var idCat:int = -1;
private var nameTEST:String = null;
private var parent:Category = null;
private var articles:ArrayCollection = new ArrayCollection;
private var children:ArrayCollection = new ArrayCollection;
public function Category(id:int, name:String, parent:Category, childlist:ArrayCollection, articles:ArrayCollection = null)
{
this.idCat = id;
this.nameTEST = name;
this.parent = parent;
this.articles = articles;
this.children = childlist;
}
public function setChildren(childList:ArrayCollection):void
{
this.children = childList;
}
public function getChildren():ArrayCollection
{
return this.children;
}
public function getName():String
{
return this.nameTEST;
}
}
Then i got a Flex service class calling BlazeDS and executing the getCategories java method. Since Flash dosn't seem to understand typed arrays, the result from that method which i get back in flex is a simple array of untyped objects (the mapping dosn't seem to work here, even tought the class category exists in flex and has the same properties).
thats the first thing. but however, i'm converting the untyped objects manually into objects of the category.as class.
the second thing is that categories have child-categories within the java object, which are also ArrayLists of the type category.java. the problem about that: my result event object only contains the first level of categories, looking into them the children are allways null. i dunno why they are empty, since they ARE part of the java object category.
and the third thing (the strangest by fast), you maybe noticed i named the properties of the category.java class strange, like idCat and nameTest instead of simply id and name. why that? because the property names of my flex result objects dont seem to change when i change the java objects properties names (result object properties are named "id" and "name" but the java class object properties are named "idCAT" and "nameTEST"). that it REALLY strange, since if i set the properties, like you see at nameTEST = "TESTNAME" it IS recogniced by flex, only the proertyNAMES dont seem to be recognized at all.
is blaze DS saving / caching the mapping configuration somewhere? how do i get it to rebuild the hole mappings IF so?
that could also explain my problem about the untyped objects i get from java, since before i changed the lists into ArrayLists they where vectors ( which blazeDS dosn't support AFAIK), and maybe not only the propertynames, but also the propertytypes are hard-mapped at some wired place and blazeds just dosn't get them refreshed.
i really like checked everything 5 times by now, even redeployed blazeds on the server to make sure no mappings left, but it didnt help at all.
ANY ideas what i could do? (exept changing to another serializer then blazeds (thats what i'm going to do if everything else fails...))
i have the same issues, but if you can warm up the tree before call the method, it will be ok.
what i mean "warm up" is you iterator the arraylist without doing anything. it is flunky why this work!
I have had similar problems with a list returned from a service not including the child elements in the list. I have found that BlazeDS can return a typed list. The two things that fixed this for me were:
a) Ensure that the returned list is of type java.util.List (not java.util.ArrayList for example).
b) Ensure that the class for the elements in the list have both public setters and getters for all entities to be returned.
For example - the following code works for me:
public class OrganisationService {
...
public List<Organisation> getOrganisations() {
List<Organisation> list = new ArrayList<Organisation>();
...
return list;
}
...
}
As mentioned elsewhere, you need to initiailize your AS3 remote objects so that it is included in the SWF during compilation.
Somewhere in your code, add:
var cat:Category = new Category();
var art:Article = new Article();
That should fix your generic object issue. (I add mine all in one spot, in an application start up method).
It turns out that a simple missmatch of the classnames was the cause of all evil. still some problems to solve, but atleast i get sub-arrays returned now, only the objects are still simple AS 3 objects and not the specified "Category" and "Article" objects, but i think thats because i dont have all methods included and mapping is failing because of that.
but thanks for your offer, i appreciate that.

Categories

Resources