Ryan H
Ryan H

Reputation: 359

Strategy for many DAOs in Spring Java

We have many DAOs in an existing project (currently with no interfaces, but that can change). Rather than wiring a Spring-managed bean for each DAO class and injecting them into the service layer, we have a DAO "factory" of sorts that looks like this:

public class DAOFactory {
private static DAOFactory daoFac;

static{
    daoFac = new DAOFactory();
}

private DAOFactory(){}

public static DAOFactory getInstance(){
    return daoFac;
}

public MyDAO1 getMyDAO1(){
    return new MyDAO1();
}

    public MyDAO2 getMyDAO2(){
    return new MyDAO2();
}
    ...

(Note that MyDAO1 and MyDAO2 are concrete classes)

This allows us to easily add/call DAO methods within the Service layer, without having to 1.) add a DAO interface as a property to the service class 2.) wire the DAO implementation into service method via configuration. (And we sometimes use multiple DAOs in one service class).

DAOFactory.getInstance().getMyDAO1().doSomething();

This strategy has worked for us so far (we haven't had much need for switching implementations), but I'm wondering if there is a better method if we were able to start new? I looked at autowiring the DAOs as beans, but I'd still need to create properties in each service class to represent those DAOs being used. And in a large project, I'm hesitant to start auto-wiring beans anyway - we need to provide visibility for all developers.

It feels like I'm flip-flopping between a.) being tightly-coupled to an implementation, but less code/config overhead and b.) being loosely coupled to interfaces, but requiring plenty of code/configuration overhead.

Is there a better way I'm missing? Something in-between? Opinions welcomed.

Upvotes: 6

Views: 3308

Answers (5)

Helder Pereira
Helder Pereira

Reputation: 5756

If you don't want to annotate your DAO classes or have configuration annotations like @Value polluting them, I see two options:

1. Create a @Configuration with DAO @Beans

@Configuration
public class DaoConfiguration {

    @Value("${db.name}")
    private String dbName;

    @Value("${foo.table}")
    private String fooTable;

    @Value("${bar.table}")
    private String barTable;

    @Bean
    private FooDao fooDao() {
        return new FooDao(dbName, fooTable);
    }

    @Bean
    private BarDao barDao() {
        return new BarDao(dbName, barTable);
    }
}

Then create an @Autowired field for the DAO you need:

@Autowired
private FooDao fooDao;

2. Create a DAO factory @Component

Useful if you need to do some cleanup when the DAOs are destroyed.

@Component
public class DaoFactory {

    @Value("${db.name}")
    private String dbName;

    @Value("${foo.table}")
    private String fooTable;

    @Value("${bar.table}")
    private String barTable;

    private FooDao fooDao;
    private BarDao barDao;

    @PostConstruct
    public void init() {
        fooDao = new FooDao(dbName, fooTable);
        barDao = new BarDao(dbName, barTable);
    }

    @PreDestroy
    public void destroy() {
        try {
            fooDao.close();
        } catch (Exception e) {
            log.error("Failed to clean up FooDao", e);
        }
        try {
            barDao.close();
        } catch (Exception e) {
            log.error("Failed to clean up BarDao", e);
        }
    }

    public FooDao fooDao() {
        return fooDao;
    }

    public BarDao barDao() {
        return barDao;
    }
}

Then create an @Autowired field for the factory in the classes you need DAOs:

@Autowired
private DaoFactory daoFactory;

And use it as:

daoFactory.barDao().findAll();

Upvotes: 0

Marvo
Marvo

Reputation: 18133

I've taken a couple of different approaches so far with my projects, and haven't really settled on what's "best." And there may not be a "best" but perhaps a "best for your needs."

First, I went with a base service class.

public abstract BaseService {
    @Autowired FooDAO fooDao;
    @Autowired BarDAO barDao;
    . . .
    . . .
    protected getFooDAO() {
        return this.fooDao;
    }
}

Then in my service classes, I can write simply

Foo foo = getFooDAO().uniqueById(id);

This works, and it keeps my services tidy from all the autowiring and accessor classes for the dao instance variables. Problem is, I've now got a copy of this base class in each of my services, which frankly, meh, not that big of a deal. But it also sort of produces a code smell because it's not using composition over inheritance where, in essence, a reason for DI is to encourage composition.

A coworker suggested a factory such as yours, and called it ServiceProvider. We autowire this into our services.

@Component
public class ServiceProvider {
    @Autowired FooDAO fooDao;
    public FooDAO getFooDAO() {
        return this.fooDao;
    }
    . . .
    // yadda yadda
}

Then we have something like what you have:

Foo foo = getServiceProvider().getFooDAO().uniqueById(id);

And that's pretty darned ugly and really doesn't lend itself to clarity. So, we've dabbled with using just the instance of the provider, and naming it something short and sweet like sp. Then we get

Foo foo = this.sp.getFooDAO().uniqueById(id);

And again, it works. And it's probably a better design. And we only autowire the DAOs in one place, rather than into each Service, even though that's not really much of a problem either way. But it makes me feel better even though Me Feeling Better isn't a project requirement (but don't cha think it oughta be?)

I've been thinking we'd combine the two. We'd change BaseService to autowire the ServiceProvider, and then wrap the ugly calls.

public abstract BaseService {
    @Autowired ServiceProvider serviceProvider;

    protected getFooDAO() {
        return this.serviceProvider.getFooDAO();
    }

    protected getBarDAO() {
        return this.serviceProvider.getBarDAO();
    }
}

Makes for nicer shorthand in my services, doesn't require me to autowire each DAO into each service which just gets clunky, in my opinion, but also doesn't have a copy of all those references in each service which, you know, is an utterly ridiculous concern.

The problem that I'm left with is stepping through code in the debugger. Stepping in and out of each of those getWhateverDAO() calls is tedious, and adding a possible step through getServiceProvider() doesn't help things either.

But that's where I'm at with this issue. Frankly, I think I spend so much time thinking about this because it's a great way of avoiding all the truly hard problems our application poses.

Upvotes: 3

AlexR
AlexR

Reputation: 115328

Good question.

I think that is is very pity that you started to use DAOFactory. Spring is a super flexible factory, so I really do not understand why do you need other one. Autowiring in spring has a lot of advantages and do not reqire interfaces, so you can easily switch to using spring to access DAOs. IMHO it does not reduce but improves the visibility for other developers.

Moreover if you are thinking about refactoring of DAO layer take a look on GenericDAO from google code: http://code.google.com/p/hibernate-generic-dao/

I had a very good experience with this library. It saves your time. You actually do not need many DAOs. You need exactly one DAO. You obviously can wrap the generic DAO from google code and add your application specific terminology and functionality. But do not add entity specific code there. The entity specific code should be at service layer. No more fragile HQL, no coupling with hibernate if you are using Hibernate criteria API. This library supports both Hibernate and JPA and its API is very simple and strong.

Upvotes: 1

matus
matus

Reputation: 1582

If you use too many DAOs in 1 service you should think about to split 1 service into more lower lever (fine grained) services

Upvotes: 0

Subin Sebastian
Subin Sebastian

Reputation: 10997

I will have all the DAO s as Spring managed components and inject them into services for loose coupling. Why do you think autowiring beans is bad in a big project.?

Just annotate each DAO class with @Component and replace MyDao mydao = factory.getmyDao() with

@Autowired MyDao myDao;

I dont see much coding/configuration overhead with it.

Upvotes: 5

Related Questions