views:

943

answers:

2

I have a foo that has a relationship to many bar's.

When I delete bar's in my system I want to keep them in the database for some crazy business reason so I just set a deleted field to true.

Can I specify in my hibernate mapping that I only want my collection to hold elements where that field is false?

+2  A: 

Hibernate provides filters to accomplish this.

Example beans:

public class Foo {
    private Long id;
    private String text;
    private Set<Bar> bars = new HashSet<Bar>();    
    // constructors, getters, setters
}

public class Bar {
    private Long id;
    private boolean deleted;
    private String text;
    // constructors, getters, setters
}

Example mappings NB: the filter element

<hibernate-mapping package="org.nkl.hib">
  <class name="Foo">
    <id name="id" column="FOO_ID">
      <generator class="sequence" />
    </id>
    <property name="text" />
    <set name="bars" cascade="all" fetch="join">
      <key column="FOO_ID" />
      <one-to-many class="Bar" />
      <filter name="deleted" condition=":deleted = deleted" />
    </set>
  </class>
  <filter-def name="deleted">
    <filter-param name="deleted" type="boolean" />
  </filter-def>
</hibernate-mapping>

<hibernate-mapping package="org.nkl.hib">
  <class name="Bar">
    <id name="id" column="BAR_ID">
      <generator class="sequence" />
    </id>
    <property name="text" />
    <property name="deleted" />
  </class>
</hibernate-mapping>

Example unit test:

public class FooBarTest {

    private static SessionFactory sessionFactory;

    @AfterClass
    public static void closeSessionFactory() {
        sessionFactory.close();
    }

    @BeforeClass
    public static void setupSessionFactory() {
        Configuration configuration = new Configuration();
        configuration.configure();
        sessionFactory = configuration.buildSessionFactory();
    }

    @Test
    public void testBarFilter() {
        doInTransaction(new Command() {
            public void execute(Session session) {
                Foo foo = new Foo("foo");
                foo.addBar(new Bar("bar1"));
                foo.addBar(new Bar("bar2"));
                foo.addBar(new Bar("bar3"));
                session.save(foo);
            }
        });

        doInTransaction(new Command() {
            public void execute(Session session) {
                Bar bar = (Bar) session.createQuery(
                        "from Bar b where b.text = 'bar2'").
                        uniqueResult();
                bar.setDeleted(true);
                session.update(bar);
            }
        });

        doInTransaction(new Command() {
            public void execute(Session session) {
                session.enableFilter("deleted").
                        setParameter("deleted", Boolean.FALSE);
                Foo foo = (Foo) session.createQuery("from Foo").
                        uniqueResult();
                assertEquals(2, foo.getBars().size());
            }
        });
    }

    private void doInTransaction(Command command) {
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        command.execute(session);
        tx.commit();
        session.close();
    }
}

interface Command {
    public void execute(Session session);
}
toolkit
+1  A: 

You can also use an SQL statement to keep stuff out, using the where attribute: Example bit of your hibernate mapping file for a set relationship:

<set> name="specialConditions" cascade="none" order-by="sortOrder, Text1" 
  where="Discriminator in ( 'SPECIAL-CURF-AGREEMENT' ) and active = 'Y'" 
  sort="unsorted" inverse="false" mutable="true" optimistic-lock="true" 
  embed-xml="true">    
  <key column="parentID" not-null="false" on-delete="noaction" /> 
  <one-to-many class="au.gov.abs.maserati.domain.entity.Condition" not-found="exception" embed-xml="true" /> 
</set>
Martlark
Cool, I didn't know this and have been using Hibernate for years now.
javashlook