Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autowiring a DAO into a domain object [duplicate]

I am coding a ribbon/achievements system for a website and I have to write some logic for each ribbon in my system. For example, you could earn a ribbon if you're in the first 2,000 people registering to the website or after 1,000 post in the forum. The idea is very similar to stackoverflow's badges, really.

So, every ribbon is obviously in the database but they also need a bit of logic to determine when a user has earned the ribbon.

In the way I coded it, Ribbon is a simple abstract class:

@Entity
@Table(name = "ribbon")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "ribbon_type")
public abstract class Ribbon
{
    @Id
    @Column(name = "id", nullable = false, length = 8)
    private int id;

    @Column(name = "title", nullable = false, length = 64)
    private String title;

    public Ribbon()
    {
    }

    public abstract boolean isEarned(User user);

    // ... getters/setters...
}

You can see I define the inheritance strategy as SINGLE_TABLE (since I have to code like 50 ribbons and I don't need additional columns for any of them).

Now, a specific ribbon will be implemented like this, for example:

@Entity
public class First2000UsersRibbon extends Ribbon
{
    @Autowired
    @Transient
    private UserHasRibbonDao userHasRibbonDao;

    public First2000UsersRibbon()
    {
        super.setId(1);
        super.setTitle("Between the first 2,000 users who registered to the website");
    }

    @Override
    public boolean isEarned(User user)
    {
        if(!userHasRibbonDao.userHasRibbon(user, this))
        {
            // TODO
            // All the logic to determine whether the user earned the ribbon
            // i.e. check whether the user is between the first 2000 users who registered to the website
            // Other autowired DAOs are needed
        }
        else
        {
            return true;
        }

        return false;
    }
}

The problem is that userHasRibbonDao is null inside the isEarned() method, so a NullPointerException is thrown.

I thought that having DAOs autowired into domain objects was wrong, but in this topic they told me that it's the correct approach (Domain-Driven Design).

I shared a non-working very simple example on GitHub: https://github.com/MintTwist/TestApp (remember to change the connection details in /WEB-INF/properties/jdbc.properties and to import the test_app.sql script)

Any help very appreciated.

Thank you!

Update - Reading the first answers, it seems like my approach is completely wrong. How would you ideally structure the code given that there may be 50-70 different ribbons? Thanks

like image 717
satoshi Avatar asked Dec 16 '22 22:12

satoshi


2 Answers

I'm not saying that I agree with injecting DAOs into domain instances....but

You can wire your DAO into you domain object. But you'll have to declare your domain object in your spring application-context and obtain new instances from Spring NOT using new. Make sure that you use the prototype scope! You don't want to be getting the same singleton instance everytime!

Really this logic that you want to implement belongs in a service which is injected with the DAOs that it requires.

Perhaps you could have a service like:

@Service
public class RibbonServiceImpl implements RibbonService

  @Autowired
  private RibbonDAO ribbonDAO;

  public boolean isEarned(Ribbon ribbon, User user) {
   if(!userHasRibbonDao.userHasRibbon(user, this))
        {
            // TODO
            // All the logic to determine whether the user earned the ribbon
            // i.e. check whether the user is between the first 2000 users who registered to the website
            // Other autowired DAOs are needed
        }
        else
        {
            return true;
        }

        return false;
  }  
like image 198
Alex Barnes Avatar answered Jan 01 '23 05:01

Alex Barnes


Mark it as @Configurable - @Configurable annotion will ensure that even if the beans are created outside of Spring, the dependencies are injected

You also need to add <context:spring-configured/> in your context.

like image 21
Biju Kunjummen Avatar answered Jan 01 '23 05:01

Biju Kunjummen