Generating ID randomly and persisting it in Java efficiently - java

I need to generate a number of length 12, say variable finalId.
Out of those 12 digits, 5 digits are to be taken from another value, say partialid1.
Now finalId = partialId1(5 - digits)+ partialId2(7 digits).
I need to generate partialid2 randomly, where I can use Random class of Java.
Finally i have to insert this finalId in Database, as a Primary key.
So to make sure that newly generated finalId is not existing in Oracle Database, I need to query Oracle Database as well.
Is there any efficient way other than the one i have mentioned above to generate Id in Java and check in database before persisting it?

In general making one id from another has issues because you may be clumping two things together which would be easier just to keep separate. Specifically you may be trying to squeeze a foreign key into a primary key when you could just use two keys.
In any case if you really want to build a semi-random primary key from a stub then I would suggest doing it bitwise because that way it'll be easy to extract the original id in SQL and in Java.
As has been mentioned, if you generate a UUID then you don't really need to worry about checking if it's already used, otherwise you probably will want to.
That said the code for making your ids could look like this:
public class IdGenerator {
private SecureRandom random = new SecureRandom();
private long preservedMask;
private long randomMask;
public void init(int preservedBits) {
this.preservedMask = (1L << preservedBits) - 1;
this.randomMask = ~this.preservedMask;
}
public long makeIdFrom(long preserved) {
return (this.random.nextLong() & this.randomMask) | (preserved & this.preservedMask);
}
public UUID makeUuidFrom(long preserved) {
UUID uuid = UUID.randomUUID();
return new UUID(uuid.getMostSignificantBits(), (uuid.getLeastSignificantBits() & this.randomMask) | (preserved & this.preservedMask));
}
public boolean idsMatch(long id1, long id2) {
return (id1 & this.preservedMask) == (id2 & this.preservedMask);
}
}
Essentially this preserves a number of least significant bits in your original. That number you need to specify when you call init.

I would prefer the Java UUID.
You can get the random id using UUID using below code.
String id = UUID.randomUUID().toString().substring(0,7);
System.out.println("id "+ id);.
And can append it with your other partial id and have a unique key or primary constraint in DB, depending on the column where you want to store it as suggested by #Erwin.
Note:- we have done it in past for so many primary keys and never had a case where you id collided.

Related

Hibernate custom id generation inside transaction

I am trying to insert a list of rows(questions) to a table.(lets say 'Question_Table').
The whole process is performed in a single transaction. (ie. either i have to insert all questions or none). I am using Spring's declarative transaction.
I have customized the ID generation for Question_Table.(Ref : Custom id generation)
It works for the first question. But it wont work for the second question as the first row is un-committed and the table will be empty. I am not able to inject the DAO class into Id generator as it is not a spring managed bean(so i can have a method in DAO class that reads un-committed records).
What is the best approach to use in this situation.
Generator class
public class IdGenerator implements IdentifierGenerator, Configurable {
private String prefix = "";
private String queryKey = "";
#Override
public Serializable generate(SessionImplementor sessionImpl, Object arg1) throws HibernateException {
long count = (long)sessionImpl.getNamedQuery(queryKey).list().get(0);
System.out.println("COUNT >>> "+count);
long id = count + 1;
if(id == 4) throw new NullPointerException();
String generatedId = prefix + id;
return generatedId;
}
#Override
public void configure(Type arg0, Properties arg1, ServiceRegistry arg2) throws MappingException {
prefix=arg1.getProperty("PREFIX");
queryKey=arg1.getProperty("QUERY_KEY");
}
}
Query : select count(*) from Question_Table
As i stated in the comment, you maybe can use this approach if you did not have problem using combination of string and sequence. But the downside is the value will always increase even after you delete all record in that table.
If you insist of using count, then the solution is to define your entity id on save manually like. .save(question, "QSTN_"+(row_count + i)); but you will need to be able pass that row_count which i think is not a problem since it must be on one request.
I have no answer to your specific question but i'd like to share some considerations.
If your id generation depends on the database state, then it must be done at the database level (implementation is up to you, autoincrement, custom function or sequences, etc, etc)...
Otherwise if you do it at the application level you will necessary encounter concurrent access problems and have to mitigate it using some lock or dedicated transaction which will have a significant impact on the application performance and may become inconsistent later (when adding horizontal scalability or sharding for example).
However if you want to generate your ids in an applicative layer (which can be a very good idea) then you must have an unique, distributed system dedicated for this task which is not part of your current unit of work.
#Transactional(isolation = Isolation.READ_COMMITTED)
public AccountDto saveAccount(AccountDto accountDto) {
Long accountTypeId = accountDto.getAccountTypeId();
AccountTypes accountTypes = accountTypesDao.getById( accountTypeId ).orElseThrow( NotFoundAppException::new );
account.setAccountName( newAccountName );
account.setAccountType( accountTypes );
...
accountDao.save( account );
accountDao.flush();
// new inserted account id is in the transaction now
return createAccountDtoFrom( account );
}

Hashing algorithm to use for generating unique ids?

I have objects with the following properties:
class MyObject
{
int sourceId();
String id();
}
If I use id as the identifier, there could be collisions as there may be records with the same id but different sourceId
Therefore I'm looking into generating a hash of sourceId and id and using that to generate unique ids for each record. I was thinking of just md5ing String.valueOf(sourceId + id), but it seems that md5 collisions are not as uncommon as I'd like.
Which other algorithm would be recommended for this, something which produces a fast hash, and where it'd also be very improbable for a collision to occur?
If the id() String has a fixed length, you can simply concatenate the sourceId and the Id :
public String getUniqueID ()
{
return sourceID() + id();
}
If id() doesn't have a fixed length, you can pad it with zeroes (for example) to obtain a fixed length and then concatenate it to sourceID() as before.
Assuming this value can be a String, I'd just concatenate both values with a hyphen:
class MyObject
{
int sourceId;
String id;
String getUniqueKey() {
return sourceId+"-"+id;
}
}
Then you can obtain the original values using value.split("-");

Generate Unique Identifier in Very Big Loop - Java

the question i am going to ask is very old and i think it asked on SO 5-10 times.
but mine has a different situation.
Read my Problem before making it duplicate by so many wise (over) SO users.
I am importing CSV sheet containing 10K records in my application.
My logic works in following manner,
(1) Validate & Import Sheet
(2) Save to the database if record does not exist
Step 2 is done for each and every record of the sheet.
in the step -2 i have to generate UUID to identify a particular record later ,
in my first solution
// this might be unique in some cases
String id = UUID.randomUUID().toString();
but i checked that it does generate unique id in each case , for example
if i import 10 sheet one by one with different records in it, all 10 times i am getting
duplicate key error from database for at least 4000 times in each import and save operation,
that means that out of 10,000 key generation it generates only 6000 unique ids.
so then i generate an alphanumeric code which length is 6 , some thing like
eSv3h7
and append it to previously generated id and hence get the following id
d545f2b2-63ab-4703-89b0-f2f8eca02154-eSv3h7
after testing still there is a problem of id duplication.
I also tried several combination mentioned here and on other sites but still there is a same problem of id duplication,
Now i am wondering that this occurs only for 10k records saving in loop , actually i need to import sheet which is having 8 million records in it
so how can i solve my problem of generating a Unique id in my particular case ?
Update 1 - based on all the comments
Try this thing at you end.
loop through 1 to 10,000
generate uuid in the loop
store it somewhere in simple text file
then make a simple program to find the duplicates from them , if you do not find any one duplicate in first attempt , repeat all above steps again and again and i am sure you will find duplicates.
in the past i am also strong believer of the same thing that UUID will never generates duplicates, share me your result of above test.
Update 2 - Code
This is the method which is called by each record of the sheet to be saved by caller method' loop.
#Override
public void preSynchronizedServiceExecution(ServiceData sData,
ValueObject valueObject) throws BlfException {
PropertyVO pVO = (PropertyVO) valueObject;
ArrayList<CountyAuctionPropertyVO> capList = pVO
.getCountyAuctionPropertyList();
for (CountyAuctionPropertyVO caVO : capList) {
TadFrameworkUtil.processValueObjectKeyProperty(caVO, true);
TadFrameworkUtil.processValueObjectKeyProperty(caVO
.getPropertyLastOwner(), true);
TadFrameworkUtil.processValueObjectKeyProperty(caVO
.getPropertyLastOwner().getAdd(), true);
}
ArrayList<PropertyAminitiesVO> amList = pVO.getPropertyAminitiesList();
for (PropertyAminitiesVO pamVO : amList) {
TadFrameworkUtil.processValueObjectKeyProperty(pamVO, true);
}
ArrayList<PropertyAttributesVO> atList = pVO
.getPropertyAttributesList();
for (PropertyAttributesVO patVO : atList) {
TadFrameworkUtil.processValueObjectKeyProperty(patVO, true);
}
TadFrameworkUtil.processValueObjectKeyProperty(pVO, true);
TadFrameworkUtil.processValueObjectKeyProperty(pVO.getSiteAdd(), true);
}
Following is id generation method
public static String generateUUID() throws BlfException {
// this might be unique in some cases
String id = UUID.randomUUID().toString();
// introduce custom random string in mixing of upper and lower
// alphabets,
// which is 6 character long
// and append it to generated GUID.
String rs = randomString(6);
id = id.concat("-").concat(rs);
return id;
}
Update 3 (Method added)
public static void processValueObjectKeyProperty(ValueObject valueObject,
boolean create) throws BlfException {
String key = (String) BlfConverter.getKey(valueObject);
if (!StringUtility.isStringNonEmpty(key)) {
throw new BlfException(valueObject.getObjectName()
+ "- key property does not exist.");
}
if (create) {
String id = generateUUID();
valueObject.setProperty(key, id);
} else {
String exisitingId = valueObject.getProperty(key);
if (!StringUtility.isStringNonEmpty(exisitingId)) {
String id = generateUUID();
valueObject.setProperty(key, id);
}
}
}
The random string method is just a simple methods of 2 lines which generates alpha numeric random string of length 6.
please ask me if you need anything more so i can post here.
Update 4 (Sample genearted UUID )
d545f2b2-63ab-4703-89b0-f2f8eca02154-eSv3h7
6f06fa28-6f36-4ed4-926b-9fef86d002b3-DZ2LaE
20142d05-f456-4d72-b845-b6819443b480-xzypQr
67b2a353-e7b4-4245-90a0-e9fca8644713-AgSQZm
8213b275-2cb1-4d37-aff0-316a47e5b780-vMIwv9
and i am getting accurate result from database if i need to fetch it from there.
Thanks
Thanks for all your user who seriously study my question and spent some time to help me in solving it.
I found that the error was in Database layer of business logic foundation.
One of the Object needs to Updated but it was created using previous
existing id so that i was getting the Duplicate Primary key Error.
I develop a Unit Test for id generation and tested UUID for more than
one billion key , it is guaranteed to be unique, it is true in all
circumstances.
Thanks again to everyone.

How do I get the foreign key column value of a dependent Hibernate Entity without fetching the full entity?

I'm struggling with a problem that seems way too easy:
Setup is two Entities with a Many-To-One Relationsship in Hibernate 3:
#Entity
class M {
private N n;
#ManyToOne(fetch = FetchType.LAZY)
public N getN() { return n; }
public void setN(N n) { this.n = n; }
}
#Entity
class N {
private List<M> ms = new ArrayList<M>();
#OneToMany(mappedBy="n")
public List<M> getMs() { return ms; }
public void setMs(List<M> ms) { this.ms = ms; }
}
Easy enough. In my application, I have a list of Ms that either have an N or don't. This list is the input for a h:dataTable that shows different column content depending on whether the FK is null or not. But when I test m.getN() != null this causes hibernate to load N. How can I avoid this?
Edit: this is actually an error of mine, as pointed out by JBNizet in the comments. To at least make this useful to someone and keep with the layout above, I've rephrased the question to "How do I get the foreign key column value of a dependent Hibernate Entity without fetching the full entity?" as suggested by Aaron Digulla.
Edit 2: Turns out the new question is a duplicate of this one: How can I prevent Hibernate fetching joined entities when I access only the foreign key id? - so, close vote?
Create a projection mapping which contains M or several fields of M and e.g. id of N
Your query might liook sopething like
select new com.my.ProjectionObject(m, m.n.id) from M m where ...
How do you expect Hibernate to tell you something it doesn't know? Without loading the entity, Hibernate has no way to know whether it (still) exists.
If you step outside the Hibernate "entity mapper" box, you can query the database directly and, for example, count the number of Ns for your M.

creating unique yet ordered ,order number for a customer's order

In my java app I need to generate a unique order number for a customer's order.I thought the time of creation of order is a good enough unique value.Two orders cannot be created at the same second.
To prevent others from using the ordernumber, by guessing some creationtime value,I appended a part of hash of the creationtime string to it and made it the final order number string.
Is there any unseen pitfall in this approach?Creating the order number based on time of creation was intended to give some sort order for the created orders in the system..
The code is given here
public static String createOrderNumber(Date orderDate) throws NoSuchAlgorithmException {
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String datestring = df.format(orderDate).toString();
System.out.println("datestring="+datestring);
System.out.println("datestring size="+datestring.length());
String hash = makeHashString(datestring);//creates SHA1 hash of 16 digits
System.out.println("hash="+hash);
System.out.println("hash size="+hash.length());
int datestringlen = datestring.length();
String ordernum = datestring+hash.substring(datestringlen,datestringlen+5);
System.out.println("ordernum size="+ordernum.length());
return ordernum;
}
private static String makeHashString(String plain) throws NoSuchAlgorithmException {
final int MD_PASSWORD_LENGTH = 16;
final String HASH_ALGORITHM = "SHA1";
String hash = null;
try {
MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
md.update(plain.getBytes());
BigInteger hashint = new BigInteger(1, md.digest());
hash = hashint.toString(MD_PASSWORD_LENGTH);
} catch (NoSuchAlgorithmException nsae) {
throw(nsae);
}
return hash;
}
A sample output is
datestring=20110924103251
datestring size=14
hash=a9bcd51fc69d9225c5d96061d9c8628137df14e0
hash size=40
ordernum size=19
ordernum=2011092410325125c5d
One potential issue cn arise if your application runs on cluster of servers.
In this case if it happens that this code is executed simultanesously in two JVMs tha same orders will be generated.
If this is not the case, the unique order number generation based on the dates sounds ok to me.
I didn't really understood the meaning of hash here.
I mean from the cryptography point of view it doesn't really add a security to your code. If a "malicious" client guesses the order number, its enough to know that the SHA1 hash is applied, the algorithm itself is known, and may be applied to determine the order number.
Hope this helps
If needed an unique it should always be from a 3rd party system which is common and the receiving/calculating method should be through a synchronized method where this will happens sequential or can be generated through database system which will be almost always unique.

Categories