Hibernate: Save Double to Database as Int - java

does anyone know how I can create a Java POJO which has stores a double in memory but an integer on disk?
I would like to store these objects to disk as integers, because I have 100 million of them, and that would save space. However, sometimes my Java code manipulates the values in memory (without saving them) and turns them into real numbers
I have tried something like:
#Entity
#Table(name = "POJO")
public class POJO {
#Id
#GeneratedValue
#Column(name = "id")
int id;
#Column(name = "value")
int valueInteger; // this is weird: in the database, we want to use integers, to save space. but in memory, sometimes we use real values
double value;
public int getValueInteger() {
return valueInteger;
}
public void setValueInteger(int valueInteger) {
this.valueInteger = valueInteger;
this.value = valueInteger;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
// might want to call setValueInteger() here if consistency is required. Right now, I think it's not, so I'll leave it out.
}
}
This is not a terrible solution, except the memory footprint is larger than needed. Is there some way to have the POJO have only a double (and the ID of course), and instruct hibernate to force a double to integer conversion when it stores to the database (for instance, by rounding that double)?
Thanks for the help!

Hope this works:
#Column(name = "value")
private int intValue;
#Transient
private Double value = null;
public double getValue() {
if (value == null)
value = intValue;
return value;
}
public void setValue(double value) {
this.value = value;
this.intValue = value;
}

RDBMS tend to define columns in terms of precision and scale, rather than IEEE types and bit counts. If you just define a database column with fixed scale of 0 you will get the result you want when the DB automatically chops the number down.
You could define a user type object that holds it and converts, but if you're genuinely concerned about the memory footprint of a 32bit number, replacing that with a reference to a custom user type object isn't really a step forward. (Although with the behemoth that is hibernate lurking under your application code, worrying about primitive int values doesn't seem like a good place to focus your optimization efforts.)

OK I figured it out: Hibernate does all of the rounding and casting automatically. So no need for anything clever. Remove valueInteger. value will get saved to the database as an integer in the way you would except (its rounded value).

Related

How to store 60 integers in a class to be written/read from a database?

I'm setting up an application to keep track of scores of an archery shoot. Each shoot consists of 60 arrows ranging from 0-10. What would be the best way of setting up the class to store every arrows score, to then be written into an SQLite database?
I could set up a variable for every single arrow but this seems inefficient. What is the better way of doing this?
public class Portsmouth {
private int id;
Date date;
private int arrow1;
private int arrow2;
private int arrow3;
...
private int arrow60;
}
Simply use an integer array like this:
import java.lang.System; //imported by default
public class Portsmouth {
private int id;
Date date;
private int[] arrow;
public Portsmouth(int id, Date data, int[] arr){
this.id = id;
this.date = date;
arrow = new int[60];
System.arraycopy(arr, 0, arrow, 0, 60);
/**for(int i=0;i<60;i++){
arrow[i] = arr[i];
}**/
}
}
Use an int[] array or List<Integer> collection instead of defining the fields one by one.
The SQL that you run to instert rows into the database depends on your schema e.g. table being de-normalized with 60 columns vs 60 rows, one for each arrow. Usually one separates the domain model from the schema with a pattern like DAO.
I suggest an Arrow class which will have your score for that arrow 1-10. Then an ILIST of arrow in another class called Shoot. Then I will imagine you have another tournament and/or shooter class for which you include properties in the Shoot class. Give it a try if you like it and I can expand.
If you plan on pushing/pulling all of the information to a database, one way to move this data would be to use Java Persistence API (JPA). This way you can push or pull the data easily. One limitation of JPA is that each column in a row must have its own variable, so you would end up with many strings, as you said. Do note that JPA mandates a database key of some sort.
Fortunately, if you wanted to push your data up, it would be as easy as:
Class.forName("your.database.driver");
Connection con = DriverManager.getConnection("url", "username", "password");
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PersistenceUnitName");
EntityManager em = emf.createEntityManager();
PersistenceEntity persistenceEntity; //This is what holds all of your data
em.persist(persistenceEntity); //Push a full row of data
em.getTransaction.commit();
con.close();
You can also employ some rather clever ways to display/get access to this information when not using it directly with the database, to prevent you from calling something like Java Reflection API.
But why did you named your class Portsmouth ? It's better to make everything more usable. Maybe three classes (3 database tables).
Class Tournament{
private int id;
Date date;
String name;
String place
Shoot shoot;
}
Class Shoot {
private int id;
#OneToMany
List<Arrow> arrowList;
}
Class Arrow {
private int id;
#ManyToOne
private Shoot shoot;
private Integer orderNumber; (1-60)
private Integer result (1-10) instead of Integer can be modified to ENUM ArrowResult from 1-10
}

Using Realm.io to store money values

I'm starting to play with Realm.io in an Android app that I'm writing. In one of my data objects, I'm required to store a currency value. Previously I had stored the value internally as a BigDecimal value and then converted that too and from a double value when moving in and out of the database.
I have always been told that it is a bad idea to store currency values in a double because of the way that they are handled.
Unfortunately, Realm.io doesn't support the storage and retrieval of BigDecimal objects.
Is the best solution to write my own currency class that extends RealmObject and keeps that as a member variable of by data object?
Emanuele from Realm here.
You are right, using floats or doubles for currency is a bad idea.
We don't support BigDecimal for now, and before we do we will have to see how that plays in relation to all other language bindings since we want realm files to be compatible across all the supported platforms.
Christian's idea is good, but I see the conversion to and from String to be a bit slow. If you don't need the arbitrary precision property of BigDecimal you could use long and multiply/divide by the factor your required precision calls for. This would also save a lot of space in terms of the size of the Realm file since integer values are bit packed.
That could work, but would probably be suboptimal if do calculations on your current BigDecimal objects.
You could also use the #Ignore annotation to provide a wrapper method for your custom objects like this:
public class Money extends RealmObject {
private String dbValue;
#Ignore private BigDecimal value;
public String getDbValue() {
return dbValue;
}
public void setDbValue(String dbValue) {
this.dbValue = dbValue;
}
public BigDecimal getValue() {
return new BigDecimal(getDbValue());
}
public void setValue(BigDecimal value) {
setDbValue(value.toString());
}
}
It is not perfect as you need to expose the *dbValue() methods, but it should work.
I would also suggest going to https://github.com/realm/realm-java/issues and make a feature request for this as BigDecimal is probably one of those java classes used by so many that it could warrant native Realm support, just like Date has.
What I do is store it as long
I have defined in my application a constant like so:
public static final BigDecimal MONEY_PRECISION = new BigDecimal(1000);
and when I need to store a big decimal it goes like this:
public class MoneyClass extends RealmObject {
long _price = 0;
public void set_price(BigDecimal price) {
this._price = price.longValue() * App.MONEY_PRECISION.longValue();
}
public BigDecimal get_price() {
return new BigDecimal(_price).divide(App.MONEY_PRECISION, 0, 0);
}
}
In theory this should be faster than saving it on strings , but I haven't really looked at the realm code much
My solution:
Define Interface:
public interface RealmConvert {
void convertToDB();
void convertToObj();
}
Define Entity:
#Ignore
private BigDecimal balance;
private String balanceStr;
#Override public void convertToDB() {
if (getBalance() != null) {
setBalanceStr(getBalance().toString());
}
}
#Override public void convertToObj() {
if (getBalanceStr() != null) {
setBalance(new BigDecimal(getBalanceStr()));
}
}
Before you copyToRealm:call method convertToDB
When you need to use the entity: call method convert obj
It's not an elegant solution, but it works.
Christian Melchior's answer doesn't work in my app.

How do I map a BigDecimal in Hibernate so I get back the same scale I put in?

In Java, new BigDecimal("1.0") != new BigDecimal("1.00") i.e., scale matters.
This is apparently not true for Hibernate/SQL Server, however. If I set the scale on a BigDecimal to a particular value, save the BigDecimal to the database via Hibernate and then re-inflate my object, I get back a BigDecimal with a different scale.
For instance, a value of 1.00 is coming back as 1.000000, I assume because we're mapping BigDecimals to a column defined as NUMERIC(19,6). I can't just define the column as the required scale as I need to store both Dollar and Yen values (for example) in the same column. We need to represent the BigDecimals as numeric types in the database for the benefit of external reporting tools.
Does there exist a Hibernate UserType which maps BigDecimal "properly", or do I have to write my own?
Just for informational sake, I can tell you that the creation of the BigDecimal coming back from the database is done by the proprietary JDBC driver's implementation of the 'getBigDecimal' method of the database-specific 'ResultSet' sub-class.
I found this out by stepping thru the Hibernate source code with a debugger, while trying to find the answer to my own question.
I think this will work, I didn't test it though.
public class BigDecimalPOJO implements Serializable {
private static final long serialVersionUID = 8172432157992700183L;
private final int SCALE = 20;
private final RoundingMode ROUNDING_MODE = RoundingMode.CEILING;
private BigDecimal number;
public BigDecimalPOJO() {
}
public BigDecimal getNumber() {
return number.setScale(SCALE, ROUNDING_MODE);
}
public void setNumber(BigDecimal number) {
this.number = number.setScale(SCALE, ROUNDING_MODE);
}
}
Not sure, but you can check equality using a.compareTo(b) == 0.

What data structure should I use for object storage, for easily generating primary keys for new entries?

I'm doing a school project in Java and I the following question have arisen:
I have an entity with attributes - id, name, phone.. with id as the unique primary key. I want to store them in a data structure(such as list..). Then in the application I obtain the data for creating a new instance (name, phone..) and I want to create a new instance of the entity and store it in my data structure with a new unique id. The id shouldn't be random, it would be best if the id rised continuously with the size of the list. Also I dont want to reuse ids.
The first implementation that comes to my mind is to use ArrayList and simply set id as indexes. But ArrayList.remove(int index) after removal shifts all following elements to left. I assume that ArrayList.remove(Object o) works the same, but i would be gratefull i I'm proven wrong. Determining ids from last element would not work either. I could go through all of them but that seems inefiicient.
Thanks in advance for any help :)
You want to keep a counter for them. You could use a static value in the class (you may need to synchronize it for multi-threaded classes.)
import java.util.concurrent.atomic.AtomicInteger;
class MyClass {
// thread safe
private static final AtomicInteger safeCounter = new AtomicInteger();
private final int uniqueId; // can never change uniqueId
private String name; // the data of the class
public MyClass(String name) {
this.name = name;
this.uniqueId = MyClass.safeCounter.getAndIncrement();
}
public boolean equals(Object o) {
if(o instanceof MyClass) { // instanceof also does null check :-)
MyClass mc = (MyClass)o;
return mc.uniqueId == this.uniqueId;
}
return false;
}
public int hashCode() {
return uniqueId;
}
}
If this is for homework, or if threadsafety isn't a concern, you can use a simple static int
class MyClass {
private static int nextUniqueId() {
int result = counter;
counter++;
return result;
}
// not thread safe
private static int counter;
private final int uniqueId; // can never change uniqueId
private String name; // the data of the class
public MyClass(String name) {
this.name = name;
this.uniqueId = nextUniqueId();
}
public boolean equals(Object o) {
if(o instanceof MyClass) { // instanceof also does null check :-)
MyClass mc = (MyClass)o;
return mc.uniqueId == this.uniqueId;
}
return false;
}
public int hashCode() {
return uniqueId;
}
}
How about using a Factory that users a Strategy for generating your identifiers?
Edited to answer question about factories
A Factory is a design pattern that is used to encapsulate the creation of different types of Objects. A Strategy is another design pattern that is used to encapsulate the behavior of specific business logic that might have different rules or that might change over time.
In your case you clearly require a new Identifier for each object that needs to be unique. You also stated in your question comments above that eventually you will be storing your objects in a database, which also would most likely require you to get your identifier from your database in the long run.
Here is a smallish example of using a Factory to create your User Objects instead of just using new(). Please kindly disregard any spelling or compile mistakes, I wrote the following code with out the assistance of a compiler or IDE.
public interface UserFactory {
User createUser();
}
public interface IdentifierStrategy {
// I just picked Long for ease of use.
Long getIdentifier();
}
public class UserFactoryImpl {
private final IdentifierStrategy identifierStrategy;
public UserFactoryImpl(final IdentifierStrategy identifierStrategy) {
this.identifierStrategy = identifierStrategy;
}
public User createUser() {
Long identifier = this.identifierStrategy.getIdentifier();
User user = new User(identifier);
return user;
}
}
public class LongIdentifierStrategy implements IdentifierStrategy {
public Long getIdentifier() {
// Do something here that will return a unique long.
Long long = new Long(1);
return long;
}
}
// In the long term, you would most likely use this IdentiferStrategy
// to get your identifiers from the database.
public class JDBCIdentifierStrategy implements IdentifierStrategy {
public Long getIdentifer() {
// Get a jdbc connection from a jdbc connection pool.
// Get the next identifier from the databsae.
Long long = new Long(1);
return long;
}
}
Now, in the long run, if your requirement change for how you need to identifier your User objects, you would only need to write a new IdentifierStrategy and update your UserFactoryImpl with that new Strategy.
One important question: what's the scope of the uniqueness?
Just for the duration of a run of the application? Do you have a single thread or multiple threads, so unique across those threads? Or could there be several copies of the app running at the same time, so unique across all instances, even across many machines? Will you save the data somewhere and so need uniqueness across future runs of the program too?
Two fundamental schemes:
a). use a database, they usually offer some kind of auto-generated primary key: you insert the record, it gives you a unique key.
b). generate the key yourself, in this case: first isolate the key generation to it's own class, then you can make the generation as clever as you wish. Sketch:
some initialisation, generate an initial value, simple case it's zero, or it derives from the current date/time, or MAC address of your machine, or whatever
provide a getNextId() function, which probably needs to be synchronized if threads are involved.
A very simple scheme, which will be OK for low volume systems, just use
new Date().getTime();
You can also look for GUID generators, which produce something unique, but rather bigger than an int.
My suggestion is to have an Object Pooling for ID generation. When the entity is "deleted", the ID should be returned to the pool, and when needing a new ID, the pool should either
Give you a new ID (if old ID doesn't exists in pool) or
Create a new ID for an entity.
The problem is that you will have to create an entity management system that caters for returning the "used" ID to the pool if entity is "deleted" (bear in mind the multithreading environment, which you will need to manage).
Alternatively, use a database system which provides primary key generation (most uses AUTO_INCREMENT).

How to Generate Unique ID in Java (Integer)?

How to generate unique ID that is integer in java that not guess next number?
How unique does it need to be?
If it's only unique within a process, then you can use an AtomicInteger and call incrementAndGet() each time you need a new value.
int uniqueId = 0;
int getUniqueId()
{
return uniqueId++;
}
Add synchronized if you want it to be thread safe.
import java.util.UUID;
public class IdGenerator {
public static int generateUniqueId() {
UUID idOne = UUID.randomUUID();
String str=""+idOne;
int uid=str.hashCode();
String filterStr=""+uid;
str=filterStr.replaceAll("-", "");
return Integer.parseInt(str);
}
// XXX: replace with java.util.UUID
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
System.out.println(generateUniqueId());
//generateUniqueId();
}
}
}
Hope this helps you.
It's easy if you are somewhat constrained.
If you have one thread, you just use uniqueID++; Be sure to store the current uniqueID when you exit.
If you have multiple threads, a common synchronized generateUniqueID method works (Implemented the same as above).
The problem is when you have many CPUs--either in a cluster or some distributed setup like a peer-to-peer game.
In that case, you can generally combine two parts to form a single number. For instance, each process that generates a unique ID can have it's own 2-byte ID number assigned and then combine it with a uniqueID++. Something like:
return (myID << 16) & uniqueID++
It can be tricky distributing the "myID" portion, but there are some ways. You can just grab one out of a centralized database, request a unique ID from a centralized server, ...
If you had a Long instead of an Int, one of the common tricks is to take the device id (UUID) of ETH0, that's guaranteed to be unique to a server--then just add on a serial number.
If you really meant integer rather than int:
Integer id = new Integer(42); // will not == any other Integer
If you want something visible outside a JVM to other processes or to the user, persistent, or a host of other considerations, then there are other approaches, but without context you are probably better off using using the built-in uniqueness of object identity within your system.
Just generate ID and check whether it is already present or not in your list of generated IDs.
UUID class
Do you need it to be;
unique between two JVMs running at
the same time.
unique even if the JVM
is restarted.
thread-safe.
support null? if not, use int or long.
if only int is required then AtomicInteger can make it possible.
if String is needed then the below code should work by mixing timeStamp and
AtomicLong.
AtomicLong idCounter = new AtomicLong(100);
long timestamp = System.currentTimeMillis();
long nextLong = idCounter.incrementAndGet();
String randomId = String.valueOf(timestamp)+String.valueOf(nextLong);
Imagine you have a class called employee with these attributes:
public class Employee {
private final String name;
private int id;
private static int nextID = 1;
public Employee(String name) {
this.name= name;
id = nextID++;
}
}
Easy peasy
Unique at any time:
int uniqueId = (int) (System.currentTimeMillis() & 0xfffffff);

Categories

Resources