Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving java BitSet to DB

Working with JPA, I would like to be able to save a BitSet to the DB and pull it back of course.

Suppose I have:

@Entity
@Table(name = "myTable")
public class MyClass {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "Object_Id")
    protected long id;

    @Column(name = "Tags")
    protected BitSet tags;

... getters & setters etc...
}

Should I define a "columnDefinition" as well? I don't really understand how it is persisted (using toString()?) and moreover how does it get loaded back from the DB.

Can you please help me with this one?

Thanks!

like image 444
forhas Avatar asked Sep 12 '12 14:09

forhas


2 Answers

More efficient way (that uses int instead of byte[]) requires a pretty simple custom class:

@Entity
@Access(AccessType.FIELD)
public class SampleEntity {

    @Transient
    private IntBitSet isolationLevel = new IntBitSet(0);

    public static final int USER_BIT = 0;
    public static final int DEVICE_BIT = 1;
    // 2, 3, 4, ...

    public boolean isUserIsolated() {
        return isolationLevel.bitGet(USER_BIT);
    }

    public boolean isDeviceIsolated() {
        return isolationLevel.bitGet(DEVICE_BIT);
    }

    public void setUserIsolated(boolean b) {
        isolationLevel.bitSet(USER_BIT, b);
    }

    public void setDeviceIsolated(boolean b) {
        isolationLevel.bitSet(DEVICE_BIT, b);
    }

    @Access(AccessType.PROPERTY)
    @Column
    public int getIsolationLevel() {
        return isolationLevel.getValue();
    }

    public void setIsolationLevel(int isolationLevel) {
        this.isolationLevel = new IntBitSet(isolationLevel);
    }

    private static class IntBitSet {
        private int value;

        public IntBitSet(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public boolean bitGet(int i) {
            return ((value >> i) & 1) == 1;
        }

        public void bitSet(int i, boolean b) {
            if (b) {
                bitSet(i);
            } else {
                bitUnset(i);
            }
        }
        private void bitSet(int i) {
            value = value | (1 << i);
        }
        private void bitUnset(int i) {
            value = value & ~(1 << i);
        }
    }
}
like image 135
mirlord Avatar answered Oct 18 '22 05:10

mirlord


By default JPA uses Java serialization to persist properties of unknown Serializable types (so that you have a serialized representation stored as a byte[]).

Usually it's not what you want, because there can be more efficient ways to represent your data. For example, BitSet can be efficiently represented as a number (if its size is limited), or byte[], or something else (unfortunately, BitSet doesn't provide methods to do these conversions, therefore you need to implement them manually).

When you've decided what kind of data representation you want to have in the database you need to tell JPA to apply the necessary conversion. There are two options:

  • Implement conversion in getters and setters. For example, as follows:

    @Entity
    @Table(name = "myTable")
    @Access(AccessType.FIELD)
    public class MyClass {
        ...
        @Transient // Do not store this field
        protected BitSet tags;
    
        @Access(AccessType.PROPERTY) // Store the property instead
        @Column(name = "Tags")
        byte[] getTagsInDbRepresentation() {
            ... // Do conversion
        }
    
        void setTagsInDbRepresentation(byte[] data) {
            ... // Do conversion
        }
        ...
    }
    
  • Use provider-specific extension to perform the conversion implicitly (for example, custom types in Hibernate). This approach allows you to reuse your type conversion logic in different entities.

like image 30
axtavt Avatar answered Oct 18 '22 04:10

axtavt