I have a simple one-to-many bidirectional association between Account (one) <--> Transaction (many)
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#OneToMany(mappedBy = "account")
private List<Transaction> transactions = new ArrayList<>();
#Entity
public class Transaction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
#ManyToOne
#JoinColumn(name = "ACCOUNT_ID")
private Account account;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Transaction that = (Transaction) o;
return Objects.equals(id, that.id);
}
#Override
public int hashCode() {
return 31;
}
}
The account (ID = 1) already has one transaction. And, I'm adding adding another transaction to the account. The block of code runs within the context of a transaction. All assertions pass:
execute(entityManager -> {
Account account = entityManager.find(Account.class, 1L);
//assertThat(account.getTransactions().size(), is(1));
Transaction t1 = new Transaction();
t1.setTitle("Buy veggies");
t1.setAmount(100);
account.getTransactions().add(t1); // ###
t1.setAccount(account);
entityManager.persist(t1);
//assertThat(account.getTransactions().size(), is(2));
});
Note the line marked ###, where the many side of the collection is loaded. I expected Hibernate to issue a SELECT on the 'Transaction' entity but it didn't. I'm including the logs below:
Hibernate:
select
account0_.id as id1_0_0_,
account0_.initial_balance as initial_2_0_0_,
...
from
account account0_
where
account0_.id=?
17:06:22.970 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [1]
Hibernate:
insert
into
transaction
(id, account_id, amount, title)
values
(null, ?, ?, ?)
17:06:22.973 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [1]
17:06:22.973 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [2] as [DOUBLE] - [100.0]
17:06:22.973 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [Buy veggies]
At the end of the transaction, I verified the integrity of the data and all looks fine. The following assertion (run a new transaction context) passed:
execute(entityManager -> {
Account account = entityManager.find(Account.class, 1L);
assertThat(account.getTransactions().size(), is(2));
});
Could someone please explain why the account.getTransactions().add(t1) did not trigger a sql SELECT? Is this Hibernate's optimization or am I missing anything?
To add to the confusion, I changed the collection to Set (everything else remains the same as before).
#OneToMany(mappedBy = "account")
private Set<Transaction> transactions = new HashSet<>();
And now, I noticed what I expected in the logs - There was indeed a sql SELECT issued when account.getTransactions().add(t1) was called:
Hibernate:
select
account0_.id as id1_0_0_,
account0_.initial_balance as initial_2_0_0_,
...
from
account account0_
where
account0_.id=?
17:09:23.056 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [1]
Hibernate:
select
transactio0_.account_id as account_4_6_0_,
transactio0_.id as id1_6_0_,
transactio0_.id as id1_6_1_,
transactio0_.account_id as account_4_6_1_,
transactio0_.amount as amount2_6_1_,
transactio0_.title as title3_6_1_
from
transaction transactio0_
where
transactio0_.account_id=?
17:09:23.057 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [1]
Hibernate:
insert
into
transaction
(id, account_id, amount, title)
values
(null, ?, ?, ?)
17:09:23.059 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - [1]
17:09:23.059 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [2] as [DOUBLE] - [100.0]
17:09:23.059 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [Buy veggies]
hibernate-core 5.4.12.Final in spring-boot 2.2.5.RELEASE
When adding to a List, the current entries don't matter, if it's a Set you have to load the entries so that you can verify that you wouldn't try adding a duplicate value. The default FetchType of #OneToMany is FetchType.LAZY, hence the missing select statement. Accessing the List entries triggers a fetch.
Related
I am facing a performance issue in Akka remoting. I have 2 actors Actor1 and Actor2. The message sending between the actor is synchronous ask request from Actor1 to Actor2 and the response back from Actor2 to Actor1. Below is the sample code snippets and config of my Actor:
Actor1.java:
object Actor1 extends App {
val conf = ConfigFactory.load()
val system = ActorSystem("testSystem1", conf.getConfig("remote1"))
val actor = system.actorOf(Props[Actor1].withDispatcher("my-dispatcher"), "actor1")
implicit val timeOut: Timeout = Timeout(10 seconds)
class Actor1 extends Actor {
var value = 0
var actorRef: ActorRef = null
override def preStart(): Unit = {
println(self.path)
}
override def receive: Receive = {
case "register" =>
actorRef = sender()
println("Registering the actor")
val time = System.currentTimeMillis()
(1 to 300000).foreach(value => {
if (value % 10000 == 0) {
println("message count -- " + value + " --- time taken - " + (System.currentTimeMillis() - time))
}
Await.result(actorRef ? value, 10 seconds)
})
val totalTime = System.currentTimeMillis() - time
println("Total Time - " + totalTime)
}
}
}
Actor2.java:
object Actor2 extends App {
val conf = ConfigFactory.load()
val system = ActorSystem("testSystem1", conf.getConfig("remote2"))
val actor = system.actorOf(Props[Actor2].withDispatcher("my-dispatcher"), "actor2")
implicit val timeOut: Timeout = Timeout(10 seconds)
actor ! "send"
class Actor2 extends Actor {
var value = 0
var actorSelection: ActorSelection = context.actorSelection("akka://testSystem1#127.0.0.1:6061/user/actor1")
override def receive: Receive = {
case "send" =>
actorSelection ! "register"
case int: Int => {
sender() ! 1
}
}
}
}
application.conf:
remote1 {
my-dispatcher {
executor = "thread-pool-executor"
type = PinnedDispatcher
}
akka {
actor {
provider = remote
}
remote {
artery {
transport = tcp # See Selecting a transport below
canonical.hostname = "127.0.0.1"
canonical.port = 6061
}
}
}
}
remote2 {
my-dispatcher {
executor = "thread-pool-executor"
type = PinnedDispatcher
}
akka {
actor {
provider = remote
}
remote {
artery {
transport = tcp # See Selecting a transport below
canonical.hostname = "127.0.0.1"
canonical.port = 6062
}
}
}
}
Output:
message count -- 10000 --- time taken - 5871
message count -- 20000 --- time taken - 9043
message count -- 30000 --- time taken - 12198
message count -- 40000 --- time taken - 15363
message count -- 50000 --- time taken - 18649
message count -- 60000 --- time taken - 22074
message count -- 70000 --- time taken - 25487
message count -- 80000 --- time taken - 28820
message count -- 90000 --- time taken - 32118
message count -- 100000 --- time taken - 35634
message count -- 110000 --- time taken - 39146
message count -- 120000 --- time taken - 42539
message count -- 130000 --- time taken - 45997
message count -- 140000 --- time taken - 50013
message count -- 150000 --- time taken - 53466
message count -- 160000 --- time taken - 57117
message count -- 170000 --- time taken - 61246
message count -- 180000 --- time taken - 65051
message count -- 190000 --- time taken - 68809
message count -- 200000 --- time taken - 72908
message count -- 210000 --- time taken - 77091
message count -- 220000 --- time taken - 80855
message count -- 230000 --- time taken - 84679
message count -- 240000 --- time taken - 89089
message count -- 250000 --- time taken - 93132
message count -- 260000 --- time taken - 97360
message count -- 270000 --- time taken - 101442
message count -- 280000 --- time taken - 105656
message count -- 290000 --- time taken - 109665
message count -- 300000 --- time taken - 113706
Total Time - 113707
Is there any wrong I am doing here?. Any observation or suggestion to improve the performance?
The main issue I see with the code is Await.result(). That is a blocking operation, and will most likely affect performance.
I suggest collecting the results in a fixed array / list, use an integer as an array, and consider it complete when the expected number of responses have been received.
I have this code in Groovy;
def execution = []
def executor =[:]
for(loopcount=1;loopcount<4;loopcount++){
executor.executor = 'jmeter'
executor.scenario = 'scenario' + loopcount
println executor.scenario
executor.concurrency = 2
execution.add(executor)
}
execution.each{
println executor.scenario
}
It is a list of three maps, all the same apart from the scenario suffix increments. I am expecting;
scenario1
scenario2
scenario3
scenario1
scenario2
scenario3
But I get;
scenario1
scenario2
scenario3
scenario3
scenario3
scenario3
It's definitely adding three different maps in the list because the .each command is returning three values. And they're definitely different values in executor.scenario because the println in the loop is giving the correct '1, 2, 3' count. But why don't they stay as different values in the list?
I've also tried execution.push(executor) but that gives the same results. For context, this yaml is what I'm aiming for eventually;
---
execution:
- executor: "jmeter"
scenario: "scenario1"
concurrency: 2
- executor: "jmeter"
scenario: "scenario2"
concurrency: 2
- executor: "jmeter"
scenario: "scenario3"
concurrency: 2
And apart from the scenario count the rest of it works fine.
problem:
def execution = []
def executor =[:]
for(loopcount=1;loopcount<4;loopcount++){
execution.add(executor) // <<-- this line adds the same variable to the list 4 times
}
to fix this - declare executor inside the for loop
def execution = []
for(loopcount=1;loopcount<4;loopcount++){
def executor =[:] // <<-- creates a new object in a loop
execution.add(executor) // <<-- adds new object to a list
}
probably to make it more clear, let me specify what [] and [:] means:
def execution = new ArrayList()
for(loopcount=1;loopcount<4;loopcount++){
def executor = new LinkedHashMap()
execution.add(executor)
}
however you could declare the variable before the loop but you have to assign a new object into it inside loop
def execution = []
def executor
for(loopcount=1;loopcount<4;loopcount++){
executor = [:]
execution.add(executor)times
}
The (smart) contract function to register a new group looks as follows:
async registerGroup(name, members, min, max, m, updateInterval) {
...
}
What is the meaning of min,max, m and updateInterval in the above?
name is the name of the group
members is the list of member added to the group at initialization. The list contains probably the public keys.
min and max set the minimum and maximum number of members, min should be >= 3.
m that is the minimum vote weight a request transaction must get.
m sets the total weight of votes required to activate a group transaction the group, the check can be found in the asch/src/contract/group.js file in the activate() function:
const group = await app.sdb.load('Group', account.name)
if (totalWeight < group.m) return 'Vote weight not enough'
Notice that m also can be set when adding a new group member with group.addMember:
async addMember(address, weight, m) {
...
if (m) {
const group = await app.sdb.load('Group', this.sender.name)
if (!group) return 'Group not found'
group.m = m
app.sdb.update('Group', { m }, { name: this.sender.name })
}
...
}
The updateInterval is unclear till now. Possible related to the time a group member should lock it's XAS.
I have the following Named query on my spring-data repository:
#Query("FROM Pedido p JOIN FETCH p.status ps WHERE ps.status IN (?1) AND ps.id IN (SELECT MAX(ps2.id) FROM PedidoStatus ps2 GROUP BY ps2.pedido)")
I'm trying to achieve the same result using the Criteria API and spring-data Specifications, this is what I have so far:
public static Specification<Pedido> byUltimoStatus(final List<PedidoStatus.StatusPedido> ultimoStatus) {
return new Specification<Pedido>() {
public Predicate toPredicate(Root<Pedido> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Expression<PedidoStatus.StatusPedido> status = root.join("status").get("status");
Predicate predicateByStatus = status.in(ultimoStatus);
final Subquery<Long> subQuery = query.subquery(Long.class);
final Root<PedidoStatus> ps = subQuery.from(PedidoStatus.class);
Expression<Long> psId= ps.get("id");
Expression<Long> maxId = builder.max(psId);
subQuery.select(maxId);
subQuery.groupBy(ps.get("pedido").get("id"));
Predicate predicateByUltimoStatus = builder.in(root.join("status").get("id")).value(subQuery);
return builder.and(predicateByStatus, predicateByUltimoStatus);
}
};}
It's still not working, looks like there is an extra
INNERJOIN PedidoStatus
in the result query.
This is the result of the #Query:
select ... from Pedido pedido0_ inner join PedidoStatus status1_ on pedido0_.id=status1_.pedido where (status1_.status in (? , ?)) and (status1_.id in (select max(pedidostat2_.id) from PedidoStatus pedidostat2_ group by pedidostat2_.pedido))
And this is the result of the Criteria API:
select ... from Pedido pedido0_ inner join PedidoStatus status1_ on pedido0_.id=status1_.pedido inner join PedidoStatus status2_ on pedido0_.id=status2_.pedido where (pedido0_.id is not null) and status1_.status IN (?, ?) and (status2_.id in (select max(pedidostat3_.id) from PedidoStatus pedidostat3_ group by pedidostat3_.pedido))
Knowing that this is a very old question, it looks to me like the reason for the duplicate INNERJOIN in the query generated by a CriteriaQuery is that the code building the query, does actually invoke root.join("status") twice. The result of the first invocation should be saved into a local variable, so you can reuse it, instead of joining twice.
First you do:
Expression<PedidoStatus.StatusPedido> status = root.join("status").get("status");
And later you do:
Predicate predicateByUltimoStatus = builder.in(root.join("status").get("id")).value(subQuery);
for some reason i can't tell, there is exception when i try to get a list from CriteriaQuery using subquery. some one please help!!!
here is the code:
public String[] getProductsDistinctBySubQueriesName(String category) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = builder.createTupleQuery();
//subquery
Subquery<Integer> subqueries = criteria.subquery(Integer.class);
Root<Productscategory> productCategory = subqueries.from(Productscategory.class);
subqueries.select(productCategory.<Integer>get("productscategoryid"))
.where(builder.equal((productCategory.<String>get("productcatgoryname")), category));
//outerquery
Root<Products> root = criteria.from(Products.class);
criteria.multiselect(root.get(Products_.productname)).distinct(true)
.where(builder.in(root.get("productscategoryid")).value(subqueries));
List<Tuple> tupleResult = em.createQuery(criteria).getResultList(); // the exception is thrown here
String[] arrayProducts = new String[tupleResult.size()];
for (int i = 0; i < tupleResult.size(); i++) {
arrayProducts[i] = (String) tupleResult.get(i).get(0);
}
return arrayProducts;
}
here is the exception
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = 'Pri' at line 1
Error Code: 1064
Error Code: 1064
Call: SELECT DISTINCT t0.productname FROM products t0 WHERE t0.productscategoryid IN (SELECT t1.productscategoryid.t1.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = ?))
bind => [1 parameter bound]
Call: SELECT DISTINCT t0.productname FROM products t0 WHERE t0.productscategoryid IN (SELECT t1.productscategoryid.t1.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = ?))
bind => [1 parameter bound]
Query: TupleQuery(referenceClass=Products sql="SELECT DISTINCT t0.productname FROM products t0 WHERE t0.productscategoryid IN (SELECT t1.productscategoryid.t1.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = ?))")
Query: TupleQuery(referenceClass=Products sql="SELECT DISTINCT t0.productname FROM products t0 WHERE t0.productscategoryid IN (SELECT t1.productscategoryid.t1.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = ?))")
at org.eclipse.persistence.internal.jpa.QueryImpl.getDetailedException(QueryImpl.java:378)
at org.eclipse.persistence.internal.jpa.QueryImpl.getDetailedException(QueryImpl.java:378)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:260)
at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:469)
at com.inventory.service.ProductsServices.getProductsDistinctBySubQueriesName(ProductsServices.java:112)
at com.inventory.service.ProductsServices.getAllByName(ProductsServices.java:211)
at com.inventory.server.InventorySocketRequest.getServiceClass(InventorySocketRequest.java:77)
at com.inventory.server.InventorySocketRequest.run(InventorySocketRequest.java:44)
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = 'Pri' at line 1
Error Code: 1064
Call: SELECT DISTINCT t0.productname FROM products t0 WHERE t0.productscategoryid IN (SELECT t1.productscategoryid.t1.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = ?))
bind => [1 parameter bound]
Query: TupleQuery(referenceClass=Products sql="SELECT DISTINCT t0.productname FROM products t0 WHERE t0.productscategoryid IN (SELECT t1.productscategoryid.t1.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = ?))")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:682)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:1991)
at org.eclipse.persistence.sessions.server.ServerSession.executeCall(ServerSession.java:570)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeSelectCall(DatasourceCallQueryMechanism.java:299)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectAllRows(DatasourceCallQueryMechanism.java:694)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRowsFromTable(ExpressionQueryMechanism.java:2738)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllReportQueryRows(ExpressionQueryMechanism.java:2675)
at org.eclipse.persistence.queries.ReportQuery.executeDatabaseQuery(ReportQuery.java:848)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1127)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:403)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1215)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1793)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1775)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1740)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
... 5 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '.productscategoryid FROM productscategory t1 WHERE (t1.productcatgoryname = 'Pri' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1053)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4120)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4052)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2503)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2794)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2155)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2322)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeSelect(DatabaseAccessor.java:1007)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:642)
... 24 more
Try to use this query instead of yours, this works perfectly with EclipseLink provider, not sure what your query is not working in eclipselink but works in hibernate, I tested the query below and you will be able to select Products based on a ProductCategoryName.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpaQuery");
EntityManager em = emf.createEntityManager();
String category = "cat2";
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Products> query = builder.createQuery(Products.class);
Root<Products> products = query.from(Products.class);
Subquery<Productscategory> squery = query.subquery(Productscategory.class);
Root<Productscategory> productCategoryRoot = squery.from(Productscategory.class);
Join<Productscategory, Products> join = productCategoryRoot.join("productsCollection");
squery.select(productCategoryRoot)
.where(builder.equal(join, products), builder.equal(productCategoryRoot.get("productcatgoryname"), category));
query.where( builder.exists(squery));
List<Products> productList = em.createQuery(query).getResultList();
for (Products product : productList) {
System.out.println(product);
}
In which category is the name of the category you are trying to search..
This solved the problem =).. If this works, don't forget to accept the answer XD.