• Home
  • RSS Feed
  • Log in

Understanding and writing Hibernate custom user types
Posted by Andrew Phillips mid-morning: November 9th, 2009

In the careers of most Spring/Hibernate developers I know, there sooner or later comes a point of no escape...they have to write a Hibernate user type. The first one is usually of the copy'n'paste variety, and by and large works perfectly well.
But when things are no longer going quite as expected - Hibernate is ignoring changes to items managed by the user type, for instance - it often becomes apparent that one doesn't sufficiently understand how these user type thingies are supposed to work. At least, that's what happened to me.
In this post, we'll be dissecting the Hibernate UserType interface, explaining the relationships between the various methods, and developing a set of base user types that capture common use cases.

The naked truth

Hibernate's UserType interface, in all it's glory (we'll be leaving the more complex EnhancedUserType to one side for the moment), looks like this:

public interface UserType {
    public int[] sqlTypes();
    public Class returnedClass();
    public boolean equals(Object x, Object y) throws HibernateException;
    public int hashCode(Object x) throws HibernateException;
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException;
     public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException;
    public Object deepCopy(Object value) throws HibernateException;
    public boolean isMutable();
    public Serializable disassemble(Object value) throws HibernateException;
    public Object assemble(Serializable cached, Object owner) throws HibernateException;
    public Object replace(Object original, Object target, Object owner) throws HibernateException;
}

Of the methods, returnedClass, nullSafeSet and nullSafeGet are probably the most self-explanatory. They are likely also the ones you tweaked for your first user type. Indeed, they are perhaps the only ones one would really want to have to deal with as a user type implementor, and making this (almost) possible is one of the main purposes of the base classes we will develop.

What about the other methods, assemble, replace and the like? These used to be the ones whose implementation one copied from the example in semi-blind faith, although now that the Javadoc for them is much more detailed they will hopefully be more straightforward to implement. Still, the following "default" implementation is still common:

@Override
public boolean isMutable() {
    return false;
}

@Override
public boolean equals(Object x, Object y) throws HibernateException {
    return ObjectUtils.equals(x, y);
}

@Override
public int hashCode(Object x) throws HibernateException {
    assert (x != null);
    return x.hashCode();
}

@Override
public Object deepCopy(Object value) throws HibernateException {
    return value;
}

@Override
public Object replace(Object original, Object target, Object owner)
        throws HibernateException {
    return original;
}

@Override
public Serializable disassemble(Object value) throws HibernateException {
    return (Serializable) value;
}

@Override
public Object assemble(Serializable cached, Object owner)
        throws HibernateException {
    return cached;
}

What's wrong with this? Apart from the clutter, nothing, really...as long as you intend your objects to be treated as immutable.

Building up a case

Right, time to leave these heady abstract hights and cook up the usual artificial example. Imagine, thus, that you want to persist an entity with a StringBuilder property for, oh, I don't know, storing a frequently-updated history, for instance.
Now, StringBuilder is, of course, serializable, so can be handled by JPA (and thus Hibernate) out of the box. But what you get in the database isn't incredibly practical: a value of "Agent 007 entered the secret hideout" might end up as

aced0005737200176a6176612e6c616e672e537472696e674275696c64
65723cd5fb145a4c6acb0300007870770400000024757200025b43b0266
6b0e25d84ac020000787000000024004100670065006e0074002000300
030003700200065006e007400650072006500640020007400680065002
000730065006300720065007400200068006900640065006f0075007478

Useful, eh?

Instead, we'll do the obvious thing and persist the builder's string representation. Following the trusty copy'n'paste approach outlined above, we end up with:

public class ReadableStringBuilderUserType implements UserType {

    public Class<StringBuilder> returnedClass() {
        return StringBuilder.class;
    }

    public int[] sqlTypes() {
        return new int[] { Types.VARCHAR };
    }

    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
            throws HibernateException, SQLException {
        String value = (String) Hibernate.STRING.nullSafeGet(resultSet, names[0]);
        return ((value != null) ? new StringBuilder(value) : null);
    }

    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index)
            throws HibernateException, SQLException {
        Hibernate.STRING.nullSafeSet(preparedStatement,
                (value != null) ? value.toString() : null, index);
    }

    /* "default" implementations */

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return ObjectUtils.equals(x, y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        assert (x != null);
        return x.hashCode();
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public Object replace(Object original, Object target, Object owner)
            throws HibernateException {
        return original;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    @Override
    public Object assemble(Serializable cached, Object owner)
            throws HibernateException {
        return cached;
    }

}

Apart from the null checks that one has to watch out for, very straightforward stuff. And it works, too! We can load a StringBuilder from the DB, and if we set a new StringBuilder it is correctly persisted. Bingo!

Not so fast

...until we try the following1, that is:

@Test
public void dehydrateModified() {
    EntityWithStringBuilderProperty holder = loadEntity(EntityWithStringBuilderProperty.class, id);
    StringBuilder builder = holder.getBuilder();

    String addition = " Bond";
    builder.append(addition);
    session.flush();
    session.evict(holder);

    StringBuilder persistedBuilder =
        ((EntityWithStringBuilderProperty) loadEntity(EntityWithStringBuilderProperty.class, id))
        .getBuilder();
    assertNotSame(builder, persistedBuilder);
    assertEquals(builder.toString(), persistedBuilder.toString());
}

Bang! The value loaded is still the original value, and the final assertion fails. From the SQL it looks like Hibernate hasn't picked up the modification, for some reason:

Hibernate: select entitywith0_.id as id0_0_, entitywith0_.builder as builder0_0_ from EntityWithStringBuilderProperty entitywith0_ where entitywith0_.id=?
Hibernate: select entitywith0_.id as id0_0_, entitywith0_.builder as builder0_0_ from EntityWithStringBuilderProperty entitywith0_ where entitywith0_.id=?

Read before you paste

Looking again at our copy'n'paste implementation, the problem seems clear: no wonder Hibernate isn't picking up changes if we declare the type to be immutable2! Well, that's easily fixed:

/* "Maybe if we make it mutable..?" */

@Override
public boolean isMutable() {
    return true;
}

...

@Override
public Object deepCopy(Object value) throws HibernateException {
    return value;
}

/* should return copies for mutables, according to the documentation */

@Override
public Object replace(Object original, Object target, Object owner)
        throws HibernateException {
    return deepCopy(original);
}

@Override
public Serializable disassemble(Object value) throws HibernateException {
    return (Serializable) deepCopy(value);
}

@Override
public Object assemble(Serializable cached, Object owner)
        throws HibernateException {
    return deepCopy(cached);
}

me != me?

Nice try, but unfortunately no cigar yet: Hibernate still fails to realise that the property has been modified. But why? Hibernate should call the user type to determine if the object being persisted is equal to the one originally loaded, and our implementation of equals(Object x, Object y) should surely correctly determine that the object has been modified.

Indeed, setting a breakpoint on equals confirms that it is called, so what is going on? The breakpoint helps identify the problem: equals is being called with both arguments being references to the same, modified, StringBuilder object! So of course equals doesn't detect the change - it would be a strange equals indeed that obeyed equals(x, x) == false.

It appears, therefore, that Hibernate is "losing track" of the originally loaded state3 of the object, which makes it impossible for the user type to detect the change. A bug in Hibernate?

No. Digging a little deeper4 into the Hibernate code, it turns out that Hibernate's snapshot of the loaded state of an object is generated by a call to the user type's deepCopy method...from which our user type is simply returning a reference to the loaded object!

Without a clean copy of the loaded state (Hibernate's "clean" copy is being updated whenever we update the loaded object), it's no surprise that Hibernate can't detect that the object has been modified.

Now with copying

For a StringBuilder, deepCopy is trivially implemented:

private static StringBuilder nullSafeToStringBuilder(Object value) {
    return ((value != null) ? new StringBuilder(value.toString()) : null);
}

/* "Maybe if we make actually create a copy..?" */

@Override
public Object deepCopy(Object value) throws HibernateException {
    return nullSafeToStringBuilder(value);
}

And lo and behold, this finally does work as expected:

Hibernate: select entitywith0_.id as id0_0_, entitywith0_.builder as builder0_0_ from EntityWithStringBuilderProperty entitywith0_ where entitywith0_.id=?
Hibernate: update EntityWithStringBuilderProperty set builder=? where id=?
Hibernate: select entitywith0_.id as id0_0_, entitywith0_.builder as builder0_0_ from EntityWithStringBuilderProperty entitywith0_ where entitywith0_.id=?

Be gone, boilerplate!

So far, so good: we have a working user type. But we also have a tremendous amount of boilerplate: for the five methods (returnedClass, sqlTypes, nullSafeGet and -Set and deepCopy) containing six lines of code that actually deal with StringBuilders there are six methods dealing with "generic" functionality.

The first step in our quest to simplify our user type is, therefore, to extract the generic stuff into a base class:

public class ReadableStringBuilderUserType extends MutableUserType {

    public Class<StringBuilder> returnedClass() {
        return StringBuilder.class;
    }

    public int[] sqlTypes() {
        return new int[] { Types.VARCHAR };
    }

    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
            throws HibernateException, SQLException {
        return nullSafeToStringBuilder(Hibernate.STRING.nullSafeGet(resultSet, names[0]));
    }

    private static StringBuilder nullSafeToStringBuilder(Object value) {
        return ((value != null) ? new StringBuilder(value.toString()) : null);
    }

    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index)
            throws HibernateException, SQLException {
        Hibernate.STRING.nullSafeSet(preparedStatement,
                (value != null) ? value.toString() : null, index);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return nullSafeToStringBuilder(value);
    }

}

Extending from MutableUserType removes the boilerplate from our user type, leaving only StringBuilder-related code. Nice!

commons-hibernate-usertype5 also contains, amongst others, ImmutableUserType and XStreamableUserType, which provides convenient (and readable!) Object-to-XML serialization.

When equals equals trouble

Performing an equals check on a StringBuilder of many thousands of characters could be an expensive operation that we'd rather avoid. It might be much cheaper, for instance, to set a "dirty" flag whenever the StringBuilder is modified and then simply check this when deciding if an update is necessary. The DirtyCheckableUserType base class is intended for this kind of use case.

Here's a (not very efficiently implemented) DirtyCheckingStringBuilder we'll use as an example:

public class DirtyCheckingStringBuilder {
    private StringBuilder value;
    private boolean valueModified;
    ...

    public DirtyCheckingStringBuilder(String value) {
        assert (value != null);
        this.value = new StringBuilder(value);
        valueModified = false;
    }

    public DirtyCheckingStringBuilder append(String addition) {
        value.append(addition);
        valueModified = true;
        return this;
    }

    ...
}

Using the DirtyCheckableUserType base class, we can then create a user type that will avoid calling equals on "dirty" builders6.

public class DirtyCheckingStringBuilderUserType extends DirtyCheckableUserType {
    ...

    @Override
    protected boolean isDirty(Object object) {
        return ((DirtyCheckingStringBuilder) object).wasModified();
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return nullSafeToStringBuilder(value);
    }

}

Usually, of course, it is likely that, if equals is expensive, then so will the deepCopy be that produced the copy to be compared against in the first place. With dirty checking, it is no longer necessary for the loaded state recorded by Hibernate to actually be a genuine copy, so you may consider simply returning the input value in deepCopy.

@Override
public Object deepCopy(Object value) throws HibernateException {
    return value;
}

However, since deepCopy is also called by the default implementations of assemble and other methods, this is likely to cause problems unless you're certain that your object will not be serialized to cache and you will not be performing merges. One possible option would be to override the implementation of these other methods to return a "real" copy7:

@Override
public Object deepCopy(Object value) throws HibernateException {
    return value;
}

@Override
protected Object realDeepCopy(Object value) throws HibernateException {
    // return an actual deep copy here
}

public Object assemble(Serializable cached, Object owner)
        throws HibernateException {
    // also safe for mutable objects
    return realDeepCopy(cached);
}

public Serializable disassemble(Object value) throws HibernateException {
    // also safe for mutable objects
    Object deepCopy = realDeepCopy(value);
    assert (deepCopy instanceof Serializable);
    return (Serializable) deepCopy;
}

public Object replace(Object original, Object target, Object owner)
        throws HibernateException {
    // also safe for mutable objects
    return realDeepCopy(original);
}
Footnotes

  1. Example source code is available at Google Code.
  2. Setting the property to a different object is picked up by Hibernate, of course.
  3. For the curious, this is held by the loadedState property of the EntityEntry for the entity. These in turn are held in the entityEntries map of the session's persistenceContext property.
  4. AbstractSaveEventListener:303
  5. You'll need to add the following to your POM if using Maven:
    <dependency>
      <groupId>com.qrmedia.commons</groupId>
      <artifactId>commons-hibernate-usertype</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    ...
    <repository>
      <id>qrmedia-snapshots</id>
      <url>http://aphillips.googlecode.com/svn/maven-repository/snapshots</url>
    </repository>
    
  6. DirtyCheckableUserType will still perform an equals check if both object passed to equals(Object x, Object y) are clean: after all, just because they are clean it does not follow, in general, that they are equal (although that does hold for a "fresh copy"/"current object" pair).
  7. Caveat: I haven't tried this. If you already know of some reason why this might not work please post a comment!
  • Share/Bookmark

Tags: Hibernate user type
Filed under Hibernate, JPA, Java | 4 Comments »



4 Responses to “Understanding and writing Hibernate custom user types”



    Tom Says:
    Posted at: November 10, 2009 at 1:36 am

    I know Hibernate and ActiveRecord really well, and I understand their complexity. Articles like this highlight them.

    I expect the noSQL movement to make real traction because of this complexity.



    Attila Says:
    Posted at: January 5, 2010 at 5:27 pm

    Thanks for the tutorial.

    I’m having problems defining the mapping required to use UserType as a composite element when trying to persist the element within a MAP. My mapping files has the following code:

    Every time I try to run the code Hibernate keeps looking for the setter/getter for the component properties in the AttributeUserType which ofcourse do not exists as AttributeUserType is a class that extends UserType (your equivalent to your ReadableStringBuilderUserType).

    I hope you could help with this issue.



    Andrew Phillips Says:
    Posted at: January 7, 2010 at 11:34 am

    @Attila: Seems the mapping file you tried to submit was filtered out. Could you paste it to ideone.com or so? I can then try to include it in your comment.



    Miguel Cohnen Says:
    Posted at: February 8, 2010 at 1:21 pm

    Hi, thank you for this great post, it really helped a lot. But I’m finding a strange problem. I’m using Seam Framework, and inside my nullSafeSet I need to do some queries to the database. To do so, I grab my entityManager using Component.getInstance(”entityManager”). This works fine if What I’m doing is merging an existent entity. But If I create a new one using persist, and I use the entityManager inside nullSafeSet method, I always get this error:
    ERROR [AssertionFailure] an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
    org.hibernate.AssertionFailure: null id in com.amadeus.websolutions.irmap.model.Airport entry (don’t flush the Session after an exception occurs)
    at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:55)

    I know it has to be something related to hibernate sessions, but I can not imagine what…

    Can you provide some help here?

    Thank you!



Leave a Reply

Click here to cancel reply.

Deployment automation for Java application running on Websphere, WebLogic and JBoss

Archives

  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009

Xebia Sites

  • Xebia Corporate
  • Xebia France
  • Xebia India

Categories

  • Java (279)
  • Agile (109)
  • General (50)
  • Testing (42)
  • Performance (42)
  • Hibernate (36)
  • Scrum (33)
  • Podcast (31)
  • Architecture (31)
  • Spring (28)
  • SOA (24)
  • Maven (22)
  • Project Management (22)
  • Flex (17)
  • JPA (17)
    • JPA implementation patterns (13)
  • Eclipse (15)
  • Quality Assurance (14)
  • Middleware (19)
  • Frameworks (13)

Tag Cloud

    Closures Hibernate Java fitnesse product owner Architecture Lean XML Scrum Maven JavaOne esb Agile Awareness Workshop IntelliJ Seam Spring Scala qcon Groovy SOA Grails Agile Performance Ajax Testing Introduction to Agile Semantic Web Functional Programming Xebia Poppendieck
medicin depression buy phentermine without a perscription aricept generic hair loss help how do you prevent bone loss urinary tract infection symptoms viagra sex domination cialis viagra cures for throat infection buy sumycin acne care new medication for cancer treatment help for sleeping problems on-line pharmacies cure snoring medications to help clot blood what is aspirin buy zestoretic bronchitis vs pneumonia back pain muscle acne face medication muscle women pain behind knee fat blocker man health arthritis natural cure woman health women insomnia cheap phentermine online cats and irritable bowel syndrome buy cialis generic online nutritional diet for osteoporosis abnormal blood clots treatments for hair loss what is zyprexa dental whitening products impotence herbs drugs for diabetes allergy prevention buy canada levitra Mentax adhd in children hair loss in woman medicines for blood clot online imitrex viagra buy free dog products clindamycin drug how to stop hair loss chloramphenicol discount drug viagra what valium does permanent hair loss heart failure medicine avapro 150mg ordering viagra online food allergies order viagra online online viagra prescription carisoprodol mg improve your skin discount erectile dysfunction medication buy xanax online buy order viagra scabies teatments information allegra vitamine b1 diazepam breast cancer support free stop smoking cipro side effects ultram cheapest treatment attention deficit disorder discount vitamins supplements how to get viagra online synthroid buy cheapest cialis zyrtec online how to clear acne preventive osteoporosis immune stimulants what is hoodia On Line Viagra getting over the pain diflucan dosage health asthma online stores hair loss products blood clot drugs colon parasites hair loss products discount medicine pravastatin buy griseofulvin tablets order indomethacin dog health products how to take a beta-blocker diazapan is valium treating cold sores chronic pain drug what is osteoporosis stress drug tooth whitening lowering cholesterol naturally legality of buying cialis online order levitra treatment for insomnia cheapest cialis index depakote overdose alprazolam condom sales treatment of yeast infection xanax sales taking viagra after cialis how to control pain new birth control chest pain health prozac prescription blood clots viagra in mexico chlamydia pill cancer drugs cold flu drugs how do i order viagra online super viagra acyclovir medicine benadryl dosage erythromycin pregnancy buy contoured condom chronic muscle pain pet health dogs treatment attention deficit disorder dental teeth whitening asthma medicine free prescription drugs herpes drug diabetes treatment buy tooth whitening gel cheap fast valium generic levitra buy cheapest viagra online lopressor drug pharmacy drug prices ultram dosing treatments for bipolar disorder neurontin withdrawal parasite medication chlamydia tips for increasing breast size ways to enhance breast what is valium used for metformin tablet order birth control hair loss for men how does xanax work treatment hepatitis c rythmol cheap acai antioxidants nexium generic blood pressure pills levitra online no prescription Levitra Online medications on line motion sickness drugs bactrim online order roxithromycin nicotine where can i order viagra immune supplements buy erexin v bph prostate allopurinol xanax for depression drug new smoking stop cheap impotence drug generic cialis delivery new treatment for depression antibiotics for cat viagra china alternative medicine cholesterol viagra dose anxiety disorder treatment severe muscle pain treatment of cancer calcium carbonate penis enlargement without pill valium maximum dosage reasons for high blood pressure energy product breast enlargement info cheap effexor building your body wrinkle cream aricept dosage alpha blocker increasing female sex drive valium depression new pain meds no rx xanax drug trileptal mg imitrex avapro 150mg medicine drugs contraception female claritin pill medication for acne med orders buy viagra internet levitra effect treatment for blood clots order sominex buy creatine buy precose cheap viagra overnight lopressor drug body building info health drugs general health and medical what is diazepam eye infections in dogs online prescription pills diclofenac tablet new medication anxiety buy citalopram medication male enhancement enhancement fat blocker medicine for throat infection order cardizem about soma health remedies for dogs generic xanax cheap zyrtec for depression medicine viagra sex domination buy acne skin care product hypnosis help study cure vaginal yeast infection weight loss supplement program muscle pain in leg how to increase erection buy viagra what is cla augmentin doses gaining muscle mass health med online heart rate treatments lopressor drug dog ear canal phentermine without prescription viagra order online weight loss glipizide diabetes astelin generic fat blocker buy gel tooth whitening cheap wellbutrin online weight loss program buy antiox anti-biotics acne skin treatmen tramadole vpxl pill drugs affecting levitra immune system support augmentin hypothyroidism medication buy erexin v uy prescription medication without a prescription buy discount order osteo arthritis online buy pilocarpine cheapest place to buy phentermine parasite treatment impotence help body fat loss viagra herb alternative constipation supplements treatment dementia adhd and medications muscle spasm relief viagra online cheap relieve upper back pain stop hair loss discount viagra online menstrual cycle problems antifungal shampoo side effects ativan gabapentin medication where can i buy viagra diazepam buy soma online clonidine dosage viagra gel top hair loss fast antibiotics cure chlamydia skin fungal infections drug zofran give up smoking alternative medicine cholesterol sleeping help best online viagra scams prednisone 10mg viagra sex domination lotensin easy weight loss pain meds without prescription over the counter drugs new high blood pressure medic generic compazine cetirizine drug order phentermine best fat blockers woman enhancement supplement drug zofran buy precose new drug treatment for cancer how to increase fertility viagra in australia benadryl dosing buy alcoholism medications order l arginine buy diazepam generic for ativan ativan prescription drugs weight loss treatment for chest pain woman health where can i buy phentermine online skin fungal infection give up smoking viagra on line hoodia information how does osteoporosis occur buy viagra online buy alcoholism medications depakote overdose klonopin pill tetracycline capsules what is high blood pressure bladder control for dogs generic for lipitor glucophage online pharmacy gabapentin dosage treating yeast infections dog health info cymbalta anxiety cheap tramadol without prescription hydrea drugs used for cancer cure for high blood pressure alcohol and valium relief from constipation liver infection treatment cialis soft zantac medication help sleep problems all natural antibiotics order medication without prescription sleep problems free hypnotherapy gaining muscle mass cheap viagra order online natural help for pain how to buy viagra drug price celebrex information otc diuretic levitra 10 mg buy medicine online pets products relief foot pain cialis without prescription med care cheapest generic cialis rapid hair loss pain medications generic side effects meds without prescriptions cat anxiety buy simplicef natural cure arthritis effects of high blood pressure lowest price generic viagra how to get birth control new breast cancer drug buy topamax blood pressure meds when are beta blockers prescribed how to get pain meds order fosamax online viagra name order viagra viagra cialis cat's eye health how to relieve lower back pain treating ear infections diazapan is valium online pain doctors high blood pressure in elderly medication to stop smoking wellbutrin dosages diabetes blood sugar levels weight loss diet pill side effects of prescribed pain pills drug list high blood pressure buy cialis online in usa ultram cost how to help osteoporosis how to use clomid discount brand viagra wellbutrin cymbalta buy pills without a prescription buy pain medicine online tab tramadol depression symptoms treatment how levitra work hypertension medications beta blockers prevent premature ejaculation xanax interactions with other medicines purchase medicine on line does alli work xenical mexico prescriptions buy sumycin uy prescription medication without a prescription ambien cost methocarbamol effects cheap beta blockers cats bladder reduce cholesterol naturally metformin tablet scabies medicine breast enhancer pills body building over 50 order viagra cheap zestril medication how to buy prescription medications online pharma kamagra drugs depression ear infection symptoms big muscle controlling blood pressure pain meds and pregnancy buy diazepam without prescription skin allergies antibiotic zoloft buy weight loss nutrition program Buy Cialis breast increase meds without prescriptions blood clots medical edema treatment for flu best hangover remedy diabetes drugs