views:

326

answers:

5

I'm looking for an efficient way to persist a set of Days of the Week. In other words, a user will select any number of days from a list of weekdays. The specific date doesn't matter, just the day of the week. I need to store this set of days in a hibernate entity.

I could store one of these:

Set<DayOfWeek>
Set<Integer>

But I'm thinking it could be simpler than that. What if I just use a single int to store the data and used bitwise operations. For instance:

Sun = 1
Mon = 2
Tue = 4
Wed = 8
Thu = 16
Fri = 32
Sat = 64

So to represent the set of Sun, Mon, Wed, Sat I would persist the integer 75. This is because 1+2+8+64 = 75.

My concern is performing queries on a field like this. Would I be able to find all entities that have Wed selected?

Does this seem like a good approach? Does anyone have a better idea?

Thanks!

A: 

this is how I would do it too

some people would just insert X rows or columns(one for each day) in a table for clarity

thing is, do you need speed or clarity?

Fredou
Well, I'd like both speed and clarity! That's why I was hoping someone on SO might have a better suggestion. But if I had to choose, I think speed might be better. It won't be that hard to understand I don't think.
Tauren
A: 

I don't think this is simpler than a Set. It uses less database space, but database space is not typically expensive. It will certainly make queries more difficult, and will also make your code less human-readable.

JacobM
Wouldn't doing this require another table and relationship? For that reason I was thinking it would be simpler. Having another relationship just for this seems like overkill to me. But I see your point, it would make it less human-readable.
Tauren
Yes, it would require another table and relationship. But I'd argue that it fundamentally *is* a relationship, and pretending it's not is going to, as I say, make it hard to follow. I'll always choose clarity over performance/space optimization (given the domain I work in).
JacobM
@JacobM, after considering your and other answers, I decided that you are right. I was going for a premature optimization that probably would never have been needed anyway. Thanks for your input.
Tauren
A: 

Storing it as a set - most likely in a separate 'days' and 'userChosenDays' table - would be the most human readable, especially if you're dealing directly with the query language. It's also the most extensible.

However, SQL does provide bitwise operators, so if you're planning on dealing with the database through a web interface or something that will have checkbox-style filters anyway, it would be easy to just query bitwise against the single column and the usability wouldn't be affected.

The space and speed differences, assuming the database is properly indexed and normalized, should be negligable, and I'd recommend the storing a full set approach because maintainability is usually more important than minor performance gains.

Tanzelax
+1  A: 

The bit approach would indeed make querying for all entities with a particular weekday difficult.

The natural way to query this in SQL or HQL would involve having a SQL function doing the decode using SQL bit operations. Or if you're using Criteria and Restrictions you could build an IN clause matching all numbers with the appropriate bit set. Neither of these is pretty...

You could alternatively map it with seven separate Boolean fields, which would make the queries easy but the Java entity code ugly.

I'd go with creating a Weekday enum and putting a Set<Weekday> in your entity, which will create an additional table, but keep the Java entity sensible and querying relatively easy. And I'd probably map that Set with eager (FetchMode.JOIN) fetching, as the set's only going to have seven elements at most.

A Map<Weekday, Boolean> is also possible, but I'd certainly skip this one!

Don Roby
+3  A: 

Hi,

You could use Enum to set up days of the week

public enum DAYS_OF_THE_WEEK {
    SUN,
    MON,
    TUE,
    WED,
    THU,
    FRI,
    SAT;
}

Now you can use a collection of value type because of Hibernate supports it.

@CollectionOfElements
@Enumerated(EnumType.STRING)
@JoinTable(
    name="SELECTED_DAYS_OF_THE_WEEK",
    joinColumns=@JoinColumn(name="<OWNING_ENTITY_ID_GOES_HERE>")
)
public Set<DAYS_OF_THE_WEEK> getSelectedDays() {
    return this.selectedDays;
}

Do not forget the lifespan of a composite element or a value-type instance is bounded by the lifespan of the owning entity instance.

As said:

Would I be able to find all entities that have Wed selected ?

Yes

select distinc OwningEntity _owningEntity inner join fetch _owningEntity.selectedDays selectedDay where selectedDay = :selectedDay

query.setParameter("selectedDay", DAYS_OF_THE_WEEK.WED);

query.list();

Added to original answer: how do i implement FetchingStrategy

Suppose the following model

@Entity
public class Customer {

    private List<Order> orderList = new ArrayList<Order>();

    // getter's and setter's

}

Now our interface CustomerRepository

public interface CustomerRepository {

    Customer getById(Integer id, CustomerFetchingStrategy fetchingStrategy);
    List<Customer> getAll(CustomerFetchingStrategy fetchingStrategy);

    public static enum CustomerFetchingStrategy {
        PROXY,
        CUSTOMER,
        CUSTOMER_WITH_ORDERS;         
    }

}

Our implementation

import static br.com.app.CustomerRepository.CustomerFetchingStrategy;

public class CustomerRepositoryImpl implements CustomerRepository {

    // Usually Spring IoC or Seam @In-jection or something else
    private SessionFactory sessionFactory;

    public Customer getById(Integer id, CustomerFetchingStrategy fetchingStrategy) {
        switch(fetchingStrategy) {
            case PROXY:
                return (Customer) sessionFactory.getCurrentSession().load(Customer.class, id);
            case CUSTOMER:
                return (Customer) sessionFactory.getCurrentSession().get(Customer.class, id);
            case CUSTOMER_WITH_ORDERS:
                return (Customer) sessionFactory.getCurrentSession().createQuery("from Customer c left join fetch c.orderList where c.id = :id")
                                  .setParameter("id", id)
                                  .list().get(0);
        }
    }

    public List<Customer> getAll(CustomerFetchingStrategy fetchingStrategy) {
        // Same strategy as shown above
    }

} 

So whether some Use Case only needs CUSTOMER, i call

import static br.com.app.CustomerRepository.CustomerFetchingStrategy;

public class SomeController {

    // Again Spring Ioc or Seam @In-jection
    private CustomerRepository customerRepository;

    public void proccessForm(HttpServletRequest request, HttpServletResponse response) {
        request.setParameter("customer", customerRepository.getById(Integer.valueOf(request.getParameter("customerId"))), CUSTOMER);
    }
}

I hope it can be useful to you

regards,

Arthur Ronald F D Garcia
@Arthur - I'm already using an enum for the weekdays, but hadn't thought to use CollectionOfElements. Thanks for reminding about it! Do you think it makes sense to use the following annotation optimizations? @CollectionOfElements(fetch=FetchType.EAGER) @Enumerated(EnumType.STRING) @Column(length=3) @JoinTable(...). I was getting a column of varchar(255) before and now just varchar(3). And @donroby suggests doing an eager fetch.
Tauren
@Tauren I am not sure when you set up your column length equal to 3, it will impact performance. VARCHAR stands for VARIABLE CHAR, so if you set up a STATIC CHAR, i think it would be better to. Try to use @Column(columnDefinition="CHAR (3)") instead. fetch=FetchType.EAGER can impact performance because of, at some times, you don not want a fully initialized collection of DAYS_OF_THE_WEEK. It would be better to use HQL in order to retrieve what you really want (FetchingStrategy).
Arthur Ronald F D Garcia
@Arthur - thanks for the advice!
Tauren
@Tauren Added to original answer how i implement FetchingStrategy
Arthur Ronald F D Garcia
how about using EnumSet<DAYS_OF_THE_WEEK> or will this not play nice with hibernate?
Ray Tayek
@Ray Tayek I do not know. I need to check. Thank you, anyway
Arthur Ronald F D Garcia
@Arthur Thanks for the addition to your answer.
Tauren