0

How do i accomplish getting the this interface method to work? i am using a MySQL DB if that matters...

public interface PersonRoleRepository extends CrudRepository<PersonRole,Long>{
  //This causes null entity error from hibernate even though the SQL works outside hibernate
  @Query(value="select * from Role r left outer join Person_Role pr on r.id = pr.role_id and pr.person_id = ? order by pr.expires_date desc", nativeQuery = true)
  List<PersonRoleDto> getAllRolesAndPersonsStatusWithEachRole(int personId);


}

Here is the SQL query that returns what i want in SQL Workbench...

Select r.*, pr.*
from
Role r
left outer join person_role pr on r.id = pr.role_id and pr.person_id = ?
order by pr.expires_date desc;

Important MySQL database structure...

Table: Role
role_id bigint
name varchar
description varchar
...

Table: Person
person_id bigint
first_name varchar
last_name varchar
...

Table Person_Role_Link
person_role_id bigint
role_id bigint
person_id bigint
...
alter table person_Role_Link add constraint l1 foreign key (person_id) references Person(person_id)
alter table person_Role_Link add constraint l2 foreign key (role_id) references Role(role_id)

Here is the entity info...

@Entity
@Table(name="Role")
@EntityListeners(AuditEntityListener.class)
public class Role extends AbstractAuditEntity {
  private static final long serialVersionUID= 44543543543543454543543L;
  @id
  @GeneratedValue(strategy=GenerationType.AUTO)
  @Column(name="role_id")
  private long id;

  @NotNull
  private String fullName  
  ...

  @OneToMany(mappedBy="role",cascade=CascadeType.ALL)
  private Set<PersonRole> personRoles = new HashSet<>();

  ...
}


@Entity
@Table(name="Person_Role_Link")
@EntityListeners(AuditEntityListener.class)
class PersonRole extends AbstractAuditEntry{
  private static final long serialVersion = 54324243242432423L;

  @id
  @GeneratedValue(strategy=GenerationType.AUTO)
  @Column(name="person_role_id")
  private long id;

  @ManyToOne
  @JoinColumn(name="person_id")
  private Person person;

  @ManyToOne
  @JoinColumn(name="role_id")
  private Role role;

  ...
}

@Entity
@Table(name="Person")
@EntityListeners(AuditEntityListener.class)
public class Person extends AbstractAuditEntity{
  private ... serialVersionUID...;

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  @Column(name="person_id")
  private Long id;
  ...
  @OneToMany(mappedBy="person", cascade=CascadeType.ALL)
  private Set<PersonRole> personRoles = new HashSet<>();
  ...
}

Just for now i made a simple interface...

public interface PersonRoleDto {
  String getFullName();
  String getDescription();
  //i want more attributes but for now i will just see if it works with the basics
}

Here is the latest HQL i tried...

public interface PersonRoleRepository extends CrudRepository<PersonRole,Long>{

  //PersistentEntity must not be null
  @Query("select r.fullName as fullName, r.description as description from Role r left join r.personRoles pr where pr.person = :person")
  List<PersonRoleDto> findAllRolesWithPersonsStatus(@Param("person") Person person);
...
}

Whether I use HQL or native SQL i get a null entity error from hibernate. Also, the SQL generated from the HQL works without error but i still get a null entity error in code and, second, the SQL generated from HQL is slightly off which makes the results off. That's why i was trying so hard to get the native SQL to work.

The relationship is used to figure out how many people are in a role and at other times what roles a person has. This is a circular relationship, i'd say. I work on an Intranet so i had to hand type everything. If there are any problems seen in my code other than with the stated native query as stated then it is most likely because i had to hand type everything and not because the code is buggy. Everything else works so far but this one thing.

All help is appreciated.

UPDATE!!!!

I think this is the answer to my problem but when i try it i still get the error: PersistentEntity must not be null!

Here is how i tried to set it up...

//Added this to the top of PersonRole entity
@SqlResultSetMapping(
  name="allRolesAndPersonsStatusWithEachRole"
  classes={
   @ConstructorResult(
     targetClass=PersonRoleStatus.class,
     columns={
       @ColumnResult(name="full_name"),
       @ColumnResult(name="description"),
       ...
     }
    )
  }
)
@NamedNativeQuery(name="PersonRole.getAllRolesAndPersonsStatusWithEachRole",
  query="Select r.*, pr.* from Role r Left Join person_role_link on r.role_id = pr.role_id and pr.person_id = :id", resultSetMapping="allRolesAndPersonsStatusWithEachRole")

Created my DTO like this...

public class RolePersonStatus {
  private String fullName;
  private String description;
  private String ...

  public RolePersonStatus(String fullName, String description, ...){
    this.fullName = fullName;
    this.description = description;
    ...
  }
}

In my repository i just have:

//No annotation because it stated that the name of the method just needed to match the native query name?!?!?
List<RolePersonStatus> findAllRolesWithPersonStatus(@Param("id" Long id);

What am i missing???????

user2052618
  • 556
  • 2
  • 7
  • 20

1 Answers1

0

Try this way:

Entities

@Entity
@Table(name = "parents")
public class Parent {

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id;

    private String name;

    @OneToMany
    private List<Child> children = new ArrayList<>();

    //...
}

@Entity
@Table(name = "children")
public class Child {

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id;

    private String name;

    @ManyToOne(optional = false)
    private Reference reference;

    @OneToMany
    private final List<Toy> toys = new ArrayList<>();

    //...
}

@Entity
@Table(name = "references")
public class Reference {

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id;

    private String description;

    //...
}

@Entity
@Table(name = "toys")
public class Toy {

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id;

    private String name;

    //...
}

DTO

public interface DemoDto {

    String getParentName();
    String getChildName();
    String getToyName();
    String getDescription();
}

Repository

public interface ParentRepo extends JpaRepository<Parent, Long> {

    @Query("select " +
                "p.name as parentName, " +
                "c.name as childName, " +
                "t.name as toyName, " +
                "r.description as description " +
            "from " +
                "Parent p " +
                "join p.children c " +
                "join c.reference r " +
                "join c.toys t " +
            "where c.id = ?1 " +
            "order by r.description desc")
    List<DemoDto> getDto(Long childId);
}

Usage

@RunWith(SpringRunner.class)
@SpringBootTest
public class ParentRepoTest {

    @Autowired
    private ParentRepo parentRepo;

    @Test
    public void getDto() throws Exception {

        List<DemoDto> dtos = parentRepo.getDto(3L);
        dtos.forEach(System.out::println);
    }
}

Result

{parentName=parent2, toyName=Toy7, childName=child3, description=Description1}
{parentName=parent2, toyName=Toy8, childName=child3, description=Description1}
{parentName=parent2, toyName=Toy9, childName=child3, description=Description1}

More info is here.

Working example.

Cepr0
  • 28,144
  • 8
  • 75
  • 101
  • i tried all kinds of variants of that now and still not getting what i need. – user2052618 May 17 '17 at 17:15
  • What exactly doesn't work? I just implemented the similar code in my app and I've got everything worked. Can you add to your question the concrete code of your entities with all fields and result DTO you expected? – Cepr0 May 17 '17 at 19:09
  • so i went back to try it similar to your way, again. it should work but i get an error: PersistentEntity must not be nul. i am using the native query and using the "as" keyword to change the database field names to be names that match my interface with getters for each. but i get the error i mentioned. i tried HQL as well and there was a null error as well. forget exactly what it was. i will probably try HQL again as well. – user2052618 May 18 '17 at 15:21
  • i work on an intranet so i will try to type the relevant entity info into the original question section soon. – user2052618 May 18 '17 at 15:28
  • Bottom line, i get null entity problems trying it with HQL and SQL. – user2052618 May 18 '17 at 17:24
  • my guess is that the problem is that the PersonRole object has Person and Role objects instead of just id's. But what i can't figure out is why my native SQL that runs in MySQL Workbench won't just get run by hibernate and then just map the select values to an interface i put in the return value and it be done. why the native sql would get a PersistentEntity is null error. – user2052618 May 18 '17 at 19:12
  • At first glance your query `select r.fullName as fullName, r.description as description from Role r left join r.personRoles pr where pr.person = :person` is correct. But It causes an error 'PersistentEntity must not be null', yeah? – Cepr0 May 18 '17 at 19:32
  • @user2052618 I prepared an equivalent [example](https://github.com/Cepr0/restdemo) for you. Look at the package [example1](https://github.com/Cepr0/restdemo/tree/master/src/main/java/restsdemo/example1) for `DemoDto` and `ParentRepo`; and see their work in the test `ParentRepoTest`. – Cepr0 May 18 '17 at 20:17
  • i think i am getting close. the problem seems to be the repository type. in your original example above you didn't show the repository interface declaration which i think is important. i'd have to extend CrudRepositry but i tried that and i get an error that MyDto is not a manged type. – user2052618 May 18 '17 at 21:54
  • One thing i am confused about is in your ParentRepo you have JpaRepository and List is returned in there and DemoDto is not in any way a Parent type. i thought they had to match. – user2052618 May 18 '17 at 22:53
  • "i thought they had to match". No they didn't. Did you read the [link](https://spring.io/blog/2016/05/03/what-s-new-in-spring-data-hopper#projections-on-repository-query-methods) I provided in the answer?.. So now all work as expected? – Cepr0 May 19 '17 at 06:24
  • I've extended my answer. If it helped you, please accept it. – Cepr0 May 19 '17 at 06:50
  • Your code is different than mine. My Role entity has a list of (your equivalent) Reference entities and my Person does to (a list of Reference entities.). So it is different than your example. I inherited this design. I think it was done this way because we use HATEOAS. Bottom line, it isn't working for me. The closest I came was using native query. When using HQL, hibernate doesn't generate the "on" like my native query. So, on top of null entity error it is not the right results (show_sql) – user2052618 May 19 '17 at 15:46
  • @user2052618 I've updated my answer and example - added `Toy` entity and its list to `Child` with OneToMany relation. – Cepr0 May 20 '17 at 21:37
  • i do appreciate you looking at this but I don't think that adding Toy is relevant to my problem. Your Reference table doesn't have a parent id and child id to be a link table in a similar manner as my schema. And to be more like my problem you shouldn't have a list of children in your parent but a list of references. – user2052618 May 22 '17 at 14:08