I am trying to use UUID as key with Mybatis. There are usually two ways:
Generate user keys in code.
Use uuid() method in mysql.
The first soluation should be a better choice for me, my question is how to implement a method to generate UUID as keys as JPA or Hibernate does? Their UUID keys is not a standard UUID.
with no dash.
rearrange the sequence for better performance.
I used`UUIDHexGenerator` from Hibernate to generate UUID now.
UUIDHexGenerator.java
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the #author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
import java.io.Serializable;
/**
* <b>uuid</b><br>
* <br>
* A <tt>UUIDGenerator</tt> that returns a string of length 32,
* This string will consist of only hex digits. Optionally,
* the string may be generated with separators between each
* component of the UUID.
*
* Mapping parameters supported: separator.
*
* #author Gavin King
*/
public class UUIDHexGenerator extends AbstractUUIDGenerator {
private String sep = "";
protected String format(int intval) {
String formatted = Integer.toHexString(intval);
StringBuffer buf = new StringBuffer("00000000");
buf.replace( 8-formatted.length(), 8, formatted );
return buf.toString();
}
protected String format(short shortval) {
String formatted = Integer.toHexString(shortval);
StringBuffer buf = new StringBuffer("0000");
buf.replace( 4-formatted.length(), 4, formatted );
return buf.toString();
}
public Serializable generate() {
return new StringBuffer(36)
.append( format( getIP() ) ).append(sep)
.append( format( getJVM() ) ).append(sep)
.append( format( getHiTime() ) ).append(sep)
.append( format( getLoTime() ) ).append(sep)
.append( format( getCount() ) )
.toString();
}
public static void main( String[] args ) throws Exception {
UUIDHexGenerator gen = new UUIDHexGenerator();
UUIDHexGenerator gen2 = new UUIDHexGenerator();
for ( int i=0; i<100; i++) {
String id = (String) gen.generate();
System.out.println(id);
String id2 = (String) gen2.generate();
System.out.println(id2);
String id3 = (String) gen2.generate();
System.out.println(id3);
}
}
}
BytesHelper.java
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the #author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
public final class BytesHelper {
private BytesHelper() {}
public static int toInt( byte[] bytes ) {
int result = 0;
for (int i=0; i<4; i++) {
result = ( result << 8 ) - Byte.MIN_VALUE + (int) bytes[i];
}
return result;
}
}
AbstractUUIDGenerator.java
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the #author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
import java.net.InetAddress;
/**
* The base class for identifier generators that use a UUID algorithm. This
* class implements the algorithm, subclasses define the identifier
* format.
*
* #see UUIDHexGenerator
* #author Gavin King
*/
public abstract class AbstractUUIDGenerator {
private static final int IP;
static {
int ipadd;
try {
ipadd = BytesHelper.toInt( InetAddress.getLocalHost().getAddress() );
}
catch (Exception e) {
ipadd = 0;
}
IP = ipadd;
}
private static short counter = (short) 0;
private static final int JVM = (int) ( System.currentTimeMillis() >>> 8 );
public AbstractUUIDGenerator() {
}
/**
* Unique across JVMs on this machine (unless they load this class
* in the same quater second - very unlikely)
*/
protected int getJVM() {
return JVM;
}
/**
* Unique in a millisecond for this JVM instance (unless there
* are > Short.MAX_VALUE instances created in a millisecond)
*/
protected short getCount() {
synchronized(AbstractUUIDGenerator.class) {
if (counter<0) counter=0;
return counter++;
}
}
/**
* Unique in a local network
*/
protected int getIP() {
return IP;
}
/**
* Unique down to millisecond
*/
protected short getHiTime() {
return (short) ( System.currentTimeMillis() >>> 32 );
}
protected int getLoTime() {
return (int) System.currentTimeMillis();
}
}
Related
I have a server running Spring boot + JPA + Hibernate. I am using MySQL database (Using InnoDb engine by default).
The implementation draws inspiration from many articles I had search on Internet.
I have implemented REST API to facilitate building a website dynamically.
I wanted to log all the API requests into a log (audit log). So when the API is called,
I store the request method name and few parameters into auditlog table in MySql.
Just before I return from the API, I store the response as well by updating the same record.
I was reviewing the code logs of Hibernate when I make API requests using the web application client as well as Postman.
I noticed that for every API, it takes on an average 150ms - 200ms for inserts and updates.
This is proving to be costly for APIs which fetch very less information.
So I want to know how I can speed up the inserts so that my inserts/updates take less than 10 -20 ms.
My Auditlog entity is
#Entity
#Table(name="auditlog")
public class AuditLog{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(nullable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
#CreatedDate
private Date created_at;
#Column(nullable = false)
#Temporal(TemporalType.TIMESTAMP)
#LastModifiedDate
private Date updated_at;
#NotBlank
private String methodName;
private String param1;
// Text field with private information like password masked
#Column(length = 65535, columnDefinition = "text")
private String request;
// Text field
#Column(length = 65535, columnDefinition = "text")
private String response;
private Integer result;
... // getters and setters
}
My AuditLogRepository is :
public interface AuditLogRepository extends JpaRepository<AuditLog, Long>{
}
In my REST API controller I am doing the following
...
AuditLog logEntry = new AuditLog();
// set all the values except generated ones like id, created_at and updated_at
logEntry.setMethodName(...);
logEntry.setParam1(...);
logEntry.setRequest(...);
// Save into the table using autowired repoitory
auditLogRepoitory.saveAndFlush(logEntry);
// ... do the operation of the API
// Update the logEntry
logEntry.setResult(...);
logEntry.setResponse(...);
auditLogRepoitory.saveAndFlush(logEntry);
...
Please help me in improving the insert and updates to the table.
Or please help in improving the code so that I can make APIs response faster.
Thanks,
Sri Prad
First tips
if you want to speed up insert/update don't user JpaRepository.save method (notice that saveAndFlush() internally calls save method).
Because JpaRepository.save internal select the entity in order to know if the entity is new or if it exists in database.
Here is the default implementation of jpaRepository.save :
#Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
I think using jdbcTemplate is the best option.
Second tips
when thinking about optimizing the inserts, it is probably useful to think about doing bulk inserts. According to mysql documentation website , The time required for inserting a row is determined by the following factors, where the numbers indicate approximate proportions:
Connecting: (3)
Sending query to server: (2)
Parsing query: (2)
Inserting row: (1 × size of row)
Inserting indexes: (1 × number of indexes)
Closing: (1)
So you can easily see how bulk insert can help you improve insert speed.
Third tips
You probably need to tune your mysql instance settings as explained in this stackeroverflow anwser
Others options
Make sur you have selected the right ID generation strategy as explained here https://dzone.com/articles/spring-boot-boost-jpa-bulk-insert-performance-by-100x
If your framework allows for it, do
START TRANSACTION
at the beginning of building the page and storing the auditing. And
COMMIT
at the end.
Recently, i read smart contracts of compound finance.
In PriceOracleProxy.sol(https://etherscan.io/address/0xe7664229833AE4Abf4E269b8F23a86B657E2338D#code)
line 3863 shows:
address constant usdcOracleKey = address(1);
i'm confusing of this address(1), what's meaning of it.
/**
* #notice address of the cUSDC contract, which we hand pick a key for
*/
address public cUsdcAddress;
/**
* #notice address of the USDC contract, which we hand pick a key for
*/
address constant usdcOracleKey = address(1);
last used:
return v1PriceOracle.assetPrices(usdcOracleKey);
Its just another way to write 0x0000000000000000000000000000000000000001,
by the same logic address(0) is 0x0000000000000000000000000000000000000000
| Grails Version: 3.0.9
| Groovy Version: 2.4.5
| JVM Version: 1.8.0_60
Hi,
I have the following GORM query involving a join between the 'Event' and 'EventCategory' domain objects with page results.
def advancedSearchWithPagedResults(int max, int offset, String search, Date startDate, Date endDate, List myEventCategories) {
// need to convert our list of ints to longs as the field they'll be compared to is a long
def listOfLongs = myEventCategories.collect {
it.toLong()
}
def wildcardSearch = search ? '%' + search + '%' : '%'
def ids = Event.createCriteria().list {
projections {
distinct 'id'
}
maxResults max
firstResult offset
or {
like("eventName", wildcardSearch)
like("address.town", wildcardSearch)
like("address.county", wildcardSearch)
}
and {
if (listOfLongs) {
eventCategories {
'in'("id", listOfLongs)
}
}
between("startDateTime", startDate, endDate)
eq("enabled", true)
}
order("startDateTime", "asc")
}
/* Get the acual events using the list of id's just obtained */
def results = Event.getAll(ids)
return results
}
However, I need to add in / merge the following MySQL query that calculates the distance of each event (in miles) from the supplied latitute and longitude (51.519159, -0.133190) and then filters out any event that is in excess of 25 miles (in the example). The events are also ordered by distance from the supplied lat/long.
SELECT
`event`.*,
( 3959 * acos( cos( radians(51.519159) ) * cos( radians( `event`.address_latitude ) )
* cos( radians(`event`.address_longitude) - radians(-0.133190)) + sin(radians(51.519159))
* sin( radians(`event`.address_latitude)))) AS distance
FROM `event`
WHERE `event`.enabled = 1
HAVING distance < 25
ORDER BY distance;
My question is how to best approach changing the GORM query to incorporate the distance calulations?
Do I need to throw out my GORM query and resort to a native HQL query? I'm hoping not.
Any thoughts would be greatly appreciated.
I'll include relevant parts of the two domain objects for completeness.
class Event implements Taggable {
static hasMany = [courses: Course,
eventCategories: EventCategory,
faqs: FAQ]
static belongsTo = [eventOrganiser: EventOrganiser]
java.util.Date dateCreated
java.util.Date lastUpdated
boolean enabled = true
String eventName
String organisersDescription
#BindingFormat('dd/MM/yyyy HH:mm')
java.util.Date startDateTime
#BindingFormat('dd/MM/yyyy HH:mm')
java.util.Date endDateTime
#BindingFormat('dd/MM/yyyy HH:mm')
java.util.Date entriesOpenDateTime
#BindingFormat('dd/MM/yyyy HH:mm')
java.util.Date entriesCloseDateTime
BigDecimal fromPrice
Address address
Contact primaryContact
static embedded = ['address','primaryContact']
// transient fields
double distanceFromUsersPostcode
....
}
class EventCategory {
static hasMany = [events:Event]
static belongsTo = [Event]
String parentCategoryName
String parentSubCategoryName
String categoryName
String description
int order
}
You can store the distance query internally within a domain class as a formula and then call upon it as if it were a property of that class.
Event class:
static mapping = {
distance formula: """
(3959 * acos(cos(radians(51.519159)) *
cos(radians(ADDRESS_LATITUDE)) * cos( radians(ADDRESS_LONGITUDE) -
radians(-0.133190)) + sin(radians(51.519159)) * sin( radians(ADDRESS_LATITUDE))))
"""
}
You may need to experiment with the formula's string (whether the new line character will cause a problem).
It also seems like several parts of the query are constants and could be factored out: acos(cos(radians(51.519159)), radians(-0.133190), sin(radians(51.519159))
Criteria Builder
You can use the distance property now like any other property:
lt('distance', 25)
What is the difference between VerificationModeFactory.times() and mockito.times()?
I thought it would be faster to ask rather than go check by myself :) I was lazy and wrong so I did the check by myself and here is the answer. It is the same thing, Mockito.Times() is internally calling the VerificationModeFactory.times()
From Mockito.class
/**
* Allows verifying exact number of invocations. E.g:
* <pre>
* verify(mock, times(2)).someMethod("some arg");
* </pre>
*
* See examples in javadoc for {#link Mockito} class
*
* #param wantedNumberOfInvocations wanted number of invocations
*
* #return verification mode
*/
public static VerificationMode times(int wantedNumberOfInvocations) {
return VerificationModeFactory.times(wantedNumberOfInvocations);
}
Title pretty much says it. I have two JSON objects and I want to know if they are equal (have all the same property values).
I could stringify them both, but I'm not sure if two equal objects will always produce the same output:
E.g:
{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"favoriteColors": ["blue", "green", "red"]
}
Is a different string from:
{
"age": 25,
"lastName": "Smith",
"firstName": "John",
"favoriteColors": ["blue", "green", "red"]
}
But as objects they have identical properties.
There is a method for this in the Flex SDK. It is in the class ObjectUtil. Here is the description from the source :
/**
* Compares the Objects and returns an integer value
* indicating if the first item is less than greater than or equal to
* the second item.
* This method will recursively compare properties on nested objects and
* will return as soon as a non-zero result is found.
* By default this method will recurse to the deepest level of any property.
* To change the depth for comparison specify a non-negative value for
* the depth parameter.
* #param a Object.
* #param b Object.
* #param depth Indicates how many levels should be
* recursed when performing the comparison.
* Set this value to 0 for a shallow comparison of only the primitive
* representation of each property.
* For example:
* var a:Object = {name:"Bob", info:[1,2,3]};
* var b:Object = {name:"Alice", info:[5,6,7]};
* var c:int = ObjectUtil.compare(a, b, 0);In the above example the complex properties of a and
* b will be flattened by a call to toString()
* when doing the comparison.
* In this case the info property will be turned into a string
* when performing the comparison.
* #return Return 0 if a and b are null, NaN, or equal.
* Return 1 if a is null or greater than b.
* Return -1 if b is null or greater than a.
* #langversion 3.0
* #playerversion Flash 9
* #playerversion AIR 1.1
* #productversion Flex 3
*/
public static function compare (a:Object, b:Object, depth:int=-1) : int;
If you don't want the whole SDK maybe you can just get this function/class and use that source.
You can see the source here. Most of the work is done in the function internalCompare.
Edit: Barış' answer is the best option, as it's tried and tested. Just in case this comes in handy for someone though:
Given that JSON values are limited to a small set of simple types, it should be possible to recurse through the properties fairly easily. Something along these lines works with your example:
private function areEqual(a:Object, b:Object):Boolean {
if (a === null || a is Number || a is Boolean || a is String) {
// Compare primitive values.
return a === b;
} else {
var p:*;
for (p in a) {
// Check if a and b have different values for p.
if (!areEqual(a[p], b[p])) {
return false;
}
}
for (p in b) {
// Check if b has a value which a does not.
if (!a[p]) {
return false;
}
}
return true;
}
return false;
}
Maybe you can convert the two objects to string then compare them
function compareJSON(a:Object, b:Object):Boolean
{
return JSON.stringify(a)===JSON.stringify(b);
}