0

I'm coding an API that connects an online db to an android app and have gotten stuck on how to create a DTO that takes data from 3 tables that are connected with each other.

It's the first API I'm coding and I'm still trying to learn good practices so any suggestions are welcome.

I've reached the point where I really don't know where else to look for an answer, since all the answers I find are explaining how to use a query to take data from a single table(might be searching for it using the wrong terminology since I'm not a native English speaker)

These are the entities I create in the API:

Employees

@Entity
@Table(name = "employees")
public class Employees {
    @Id
    @Column(name = "eid")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int eid;

    @Column(name = "first_name")
    private String first_name;

    @Column(name = "last_name")
    private String last_name;

    @Column(name = "birth_date")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date birth_date;

    @Column(name = "jid")
    private int jid;

//Getters setters constructors
}

Jobs

@Entity
@Table(name = "jobs")
public class Jobs {
    @Id
    @Column(name = "jid")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int jid;

    @Column(name = "job_name")
    private String job_name;

//Getters setters constructors
}

Credentials

@Entity
@Table(name = "credentials")
@IdClass(CredentialsCID.class)
public class Credentials {
    @Id
    @Column(name = "username")
    private String username;

    @Id
    @Column(name = "is_admin")
    private byte is_admin;

    @Column(name = "password")
    private String password;

    @Column(name = "eid")
    private int eid;

//Getters setters constructors
}

And this is the dto class

EmployeesCustomDTO

package com.example.accessingdatamysql.dto;

import com.example.accessingdatamysql.model.Contracts;
import com.example.accessingdatamysql.model.Credentials;
import com.example.accessingdatamysql.model.Employees;

public class EmployeesCustomDTO {
    private int eid;
    private String first_name;
    private String last_name;
    private String jid;
    private int hours;
    private String type;
    private byte is_admin;

    public EmployeesCustomDTO() {
    }

    public EmployeesCustomDTO(int eid, String first_name, String last_name, String jid, int hours, String type, byte is_admin) {
        this.eid = eid;
        this.first_name = first_name;
        this.last_name = last_name;
        this.jid = jid;
        this.hours = hours;
        this.type = type;
        this.is_admin = is_admin;
    }

    public int getEid() {
        return eid;
    }

    public void setEid(int eid) {
        this.eid = eid;
    }

    public String getFirst_name() {
        return first_name;
    }

    public void setFirst_name(String first_name) {
        this.first_name = first_name;
    }

    public String getLast_name() {
        return last_name;
    }

    public void setLast_name(String last_name) {
        this.last_name = last_name;
    }

    public String getJob_name() {
        return jid;
    }

    public void setJob_name(String job_name) {
        this.jid = job_name;
    }

    public int getHours() {
        return hours;
    }

    public void setHours(int hours) {
        this.hours = hours;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public byte getIs_admin() {
        return is_admin;
    }

    public void setIs_admin(byte is_admin) {
        this.is_admin = is_admin;
    }

    public EmployeesCustomDTO convertToDTO(Employees employees, Credentials credentials, Contracts contracts){
        EmployeesCustomDTO employeesCustomDTO = new EmployeesCustomDTO();
        employeesCustomDTO.setEid(employees.getEid());
        employeesCustomDTO.setFirst_name(employees.getFirst_name());
        employeesCustomDTO.setLast_name(employees.getLast_name());
        employeesCustomDTO.setJob_name(String.valueOf(employees.getJid())); // TODO: 19/12/2020 check if correct
        employeesCustomDTO.setType(contracts.getType());
        employeesCustomDTO.setHours(contracts.getHours());
        employeesCustomDTO.setIs_admin(credentials.getIs_admin());

        return employeesCustomDTO;
    }
}

Then I added the following query to the EmployeesRepo (which works as I want it to when I try it on MySQL workbench)

@Repository
public interface EmployeesRepo extends JpaRepository<Employees, Integer> {
    @Query(value = "SELECT employees.eid,employees.first_name,employees.last_name,employees.jid,contracts.hours,contracts.type,credentials.is_admin " +
            "FROM employees,credentials,contracts " +
            "WHERE employees.eid = credentials.eid AND contracts.eid = employees.eid", nativeQuery = true)
    List<EmployeesCustomDTO> getCustomErgazomenoi();
}

and the following method in its service

@Override
    public List<EmployeesCustomDTO> getCustomErgazomenoi() {
        return employeesRepo.getCustomErgazomenoi();
    }

which I then call in the controller

@GetMapping("/custom-employees")
    public List<EmployeesCustomDTO> getAllCustomEmployees(){return employeesService.getCustomErgazomenoi();}

When I send the request to the endpoint I get the following error:

From Postman:

{
    "timestamp": "2020-12-22T16:40:28.542+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "message": "",
    "path": "/employees/custom-employees"
}

From the terminal in intellij

2020-12-22 18:40:28.527 ERROR 2948 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.core.co
nvert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.example.accessingdatamysql.dto.EmployeesCustomDTO]] with root cause

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.example.accessingdatamysql.dto.EmployeesCusto
mDTO]
        at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-5.3.1.jar:5.3.1]
        at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-5.3.1.jar:5.3.1]
        at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175) ~[spring-core-5.3.1.jar:5.3.1]
        at org.springframework.data.repository.query.ResultProcessor$ProjectingConverter.convert(ResultProcessor.java:309) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.lambda$and$0(ResultProcessor.java:225) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.data.repository.query.ResultProcessor$ChainingConverter.convert(ResultProcessor.java:236) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.data.repository.query.ResultProcessor.processResult(ResultProcessor.java:156) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:158) ~[spring-data-jpa-2.4.1.jar:2.4.1]
        at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:143) ~[spring-data-jpa-2.4.1.jar:2.4.1]
        at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:152) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:131) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80) ~[spring-data-commons-2.4.1.jar:2.4.1]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:371) ~[spring-tx-5.3.1.jar:5.3.1]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:134) ~[spring-tx-5.3.1.jar:5.3.1]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.1.jar:5.3.1]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:145) ~[spring-data-jpa-2.4.1.jar:2.4.1]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.1.jar:5.3.1]
        at com.sun.proxy.$Proxy120.getCustomErgazomenoi(Unknown Source) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.1.jar:5.3.1]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-5.3.1.jar:5.3.1]
        at com.sun.proxy.$Proxy77.getCustomErgazomenoi(Unknown Source) ~[na:na]
        at com.example.accessingdatamysql.services.impl.EmployeesServiceImpl.getCustomErgazomenoi(EmployeesServiceImpl.java:45) ~[classes/:na]
        at com.example.accessingdatamysql.controllers.EmployeesController.getAllCustomEmployees(EmployeesController.java:28) ~[classes/:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.1.jar:5.3.1]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:807) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.39.jar:4.0.FR]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.1.jar:5.3.1]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.39.jar:4.0.FR]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.1.jar:5.3.1]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.1.jar:5.3.1]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.1.jar:5.3.1]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
        at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]

Even though I've found some answers on how to fix this problem I can't seem to get it to work on my API. Any suggestions?

4 Answers4

1

Can you provide the code for EmployeesCustomDTO class? It looks like you are trying to perform a DTO projection which would require you to map the SQL result to the properties in the DTO.

If you are using hibernate you can create an appropriate constructor on the DTO and update query to use the new constructor:

@Query(value = "SELECT com.example.accessingdatamysql.dto.EmployeesCustomDTO(employees.eid,employees.first_name,employees.last_name,employees.jid,contracts.hours,contracts.type,credentials.is_admin) " +
               "FROM employees,credentials,contracts " +
               "WHERE employees.eid = credentials.eid AND contracts.eid = employees.eid")
List<EmployeesCustomDTO> getCustomErgazomenoi(); 

For a detailed explanation see - https://vladmihalcea.com/the-best-way-to-map-a-projection-query-to-a-dto-with-jpa-and-hibernate/

Either way, you must provide the mapping from the SQL result set to your DTOs properties. If you are following standard naming conventions in SQL and Java the names are likely not identical - e.g. your query references first_name but I assume your DTO will be firstName.

Looks like you can also provide the mapping via @SqlResultSetMapping if you are using native queries: https://thorben-janssen.com/dto-projections/#DTO_projections_for_native_SQL_queries

Similar Stackoverflow post:

andrew.b.10
  • 56
  • 1
  • 4
  • oh yeah completely forgot to add that, I'm adding it to the post – ferociousblazze Dec 22 '20 at 16:11
  • do you mean something like this: `@Query(value = "SELECT new com.example.app.EmployeesCustomDTO(employees.eid,employees.first_name,employees.last_name,employees.jid,contracts.hours,contracts.type,credentials.is_admin) " + "FROM employees,credentials,contracts " + "WHERE employees.eid = credentials.eid AND contracts.eid = employees.eid", nativeQuery = true) List getCustomErgazomenoi();` – ferociousblazze Dec 22 '20 at 16:21
  • Yes, apologies, just noticed that you have `nativeQuery = true` - I believe this approach is based on Hibernate. You would want to remove that if trying this approach. – andrew.b.10 Dec 22 '20 at 16:23
  • I have updated the query in my answer. Can you also update your question with the full exception? – andrew.b.10 Dec 22 '20 at 16:30
  • updated, will try the ways mentioned in the articles you linked and update on the results. Thanks for the quick replies – ferociousblazze Dec 22 '20 at 16:43
0

I have long struggled to make this correct. I see now that ORM (JPA, Hibernate, at least) does not solve any problem and adds a lot of complexity and hidden problems.

Easiest solution to custom query return values for me is this approach:

//import jakarta.persistence.Tuple;

@Query(value = "SELECT com.example.accessingdatamysql.dto.EmployeesCustomDTO(employees.eid,employees.first_name,employees.last_name,employees.jid,contracts.hours,contracts.type,credentials.is_admin) " +
               "FROM employees,credentials,contracts " +
               "WHERE employees.eid = credentials.eid AND contracts.eid = employees.eid")
List<Tuple> getCustomErgazomenoi();

// and than do mapping "by hand", eplicitly without reflexion and type unsafe annotations:
final List<EmployeesCustomDTO> typedResult = getCustomErgazomenoi.stream().map(a ->
    new EmployeesCustomDTO(
      "" + a.get(0),
      a.get(1, Long.class),
      a.get(2, Long.class),
      a.get(3, Long.class),
      ....))
  .toList();
Lubo
  • 1,621
  • 14
  • 33
0

you can create a database view and annotate your EmployeesCustomDTO with @Entity and @Immutable to make it readonly and use ordinary repository and service layer for this with one difference in repository which must be annotated with @NoRepositoryBean . consider this point that if the properties of your Dto is more than usual and are general and being used in lots of services its better to use Views you can read this page it could help you

https://medium.com/@salmankhan_27014/working-with-database-views-in-spring-data-jpa-2bf260527c9e

i hope it could help you.

0

Classes are not tables. You really should define the relationships between your classes:

public class Employee {
...
    @ManyToOne(fetch = FetchType.LAZY) // or FetchType.EAGER
    @JoinColumn(name = "jid")
    private Job job;

    @OneToMany(mappedBy="employee", fetch = FetchType.LAZY)
    private final Set<Credentials> events = new HashSet<>();

...
}

...

public class Credentials {
...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "eid")
    private Employee employee;
...
}

...

Now, you have a @OneToMany relationship from Employee to Credentials, so you should do your query on Credentials:

public interface CredentialsRepo extends JpaRepository<Credentials, Integer> {
...
}

And in your service class:

@Override
public List<EmployeesCustomDTO> getCustomErgazomenoi() {
    return credentialsRepo.findAll().stream()
          .map(c -> {
                    Employee e = c.getEmployee();
                    EmployeesCustomDTO employeesCustomDTO = new EmployeesCustomDTO();
                    employeesCustomDTO.setEid(e.getEid());
                    employeesCustomDTO.setFirst_name(e.getFirst_name());
                    employeesCustomDTO.setLast_name(e.getLast_name());
                    employeesCustomDTO.setJob_name(String.valueOf(e.getJob().getName()));
                    // now I don't know anything about contracts
                    // employeesCustomDTO.setType(contracts.getType());
                    // employeesCustomDTO.setHours(contracts.getHours());
                    employeesCustomDTO.setIs_admin(c.getIs_admin());

                    return employeesCustomDTO;
          }).collect(Collectors.toList());
}
Maurice Perry
  • 9,261
  • 2
  • 12
  • 24