简体   繁体   中英

Java Hibernate Spring - Service Interface with default implementation

I have a Java database application using hibernate, with different Classes that have the same attributes (here: “active”). In an interface, there is a function that retrieves entries from a database based on such an attribute (active). So far, I was doing this:

//interface
public interface ObjSvcIntf {
   default <Entity> ArrayList<Entity> get(Boolean active);
}
//implementation 1
public class ObjCarSvc implements ObjSvcIntf {

   @SuppressWarnings("unchecked")
   @Override
   public ArrayList< ObjCar > get(Boolean active) {
       @SuppressWarnings("rawtypes")
       Query query = DB.s.createQuery("from " + ObjCar.class.getSimpleName() + " where active = :active");
       query.setParameter("active", active);

       if (!query.list().isEmpty()) {
           return (ArrayList< ObjCar >) query.list();  
       } else {
           return null;
       }


   }

//implementation 1
public class ObjPersonSvc implements ObjSvcIntf {

   @SuppressWarnings("unchecked")
   @Override
   public ArrayList< ObjPerson > get(Boolean active) {
       @SuppressWarnings("rawtypes")
       Query query = DB.s.createQuery("from " + ObjPerson.class.getSimpleName() + " where active = :active");
       query.setParameter("active ", active);

       if (!query.list().isEmpty()) {
           return (ArrayList< ObjPerson >) query.list();   
       } else {
           return null;
       }


   }

As You can see, there is a lot of redundant code in each implementing class, which I would like to avoid. What I would like instead therefore, is to have a generic default function in the interface, which will return the same for each implementation of the interface (unless overridden by the implementing class of course). Ie, something like this (except this does not work, of course):

public interface ObjSvcIntf {

   default <Entity> ArrayList<Entity> get(Boolean active) {
       @SuppressWarnings("rawtypes")
       Query query = DB.s.createQuery("from " + Entity.class.getSimpleName() + " where active = :active");
       query.setParameter("active", active);

       return (ArrayList<Entity>) query.list();
   }

}

I am lacking the proper understanding here, how to create the function in the interface in the right way, to be able to use it in different contexts/ different classes.

How can I adjust the function in the interface instead to make this happen?

You can create an abstract function to return the object of the subclass. Something like this would work. Here is example code, where interface returns as list of object of the class implementing the interface.

public interface ObjSvcIntf<E> {
    default List<E> get(Boolean active) {
        var list = new ArrayList<E>();
        list.add(self());
        return list;
    }

    E self(); // function to return the sub class instance
}
public interface ObjSvcIntf<Entity> {
    default <Entity> ArrayList<Entity> get(Boolean active) {
        @SuppressWarnings("rawtypes")
        Query query = DB.s.createQuery("from " + getImplClass().getSimpleName() + " where active = :active");
        query.setParameter("active", active);
        return (ArrayList<Entity>) query.list();
    }
    Class getImplClass();
}

And you could just provide that same class for each implementation as you provide for the generic type.

I restructured the project to separate the Interface from its implementation. Each class, extending the (abstract) implementation of the interface now sets an attribute of Type "Class" when calling the super constructor, with each function in the abstract class referring to that attribute.

Is there a better way? What are potential problems with this approach?

Interface:

public interfaceObjSvcIntf {

    <Entity> Object getById(Long id);

}

Implementing abstract Class:

public abstract class ObjSvcImpl implements ObjSvcIntf {

    public Class<?> servicedClass;

    // CONSTRUCTOR
    public ObjSvcImpl(Class<?> servicedClass) {
        this.servicedClass = servicedClass;
    }

    @Override
    public <Entity> Object getById(Long id) {
         return DB.getById(this.servicedClass, id);
    }
}

Service Class:

public class ObjCarSvc extends ObjSvcImpl {

    public ObjCarSvc() {
        super(ObjCar.class);
    }

}

Model Class:

@Entity
@Table(name = "OBJ_CAR")
public class ObjCar implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "OBJ_CAR_ID")
    private Long objCarId;

    @NotNull
    @Column(name = "NAME")
    private String name;

    // Getters and Setters
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM