I have a service method which is transactional. This method saves an Entity called FascicoloENT on the database after some updates done with mapstruct. Before the entity is saved, FascicoloRepository is called by internal business logig to get the maximum value of a column in the fascicolo Table. After the Entity is saved, onother method is called which, given the Fascicolo id, saves a new entity called RicorsoENT in the db. FacicoloENT and RicorsoENT are in a one to many jpa relation.
It happens that the java application gets stuck when the FacicoloRepository is called to get the maximum value needed by business logic. Although, from logs I can see that the application stops on a different query, when saving FascicoloENT, even though the code in debug hasn't yet reached that point...
Here is my service method:
#Override
#Transactional(rollbackFor = {Exception.class})
public FascicoloDTO updateFascicoloInCompleto(FascicoloDTO fascicolo, Long id) throws LedaException {
boolean isFirstSave = false;
var optionalFascicoloEntity = fascicoloRepository.findById(id);
if (optionalFascicoloEntity.isEmpty()) {
log.error(FascicoloApiConstants.FASCICOLO_CON_ID + " {}" + FascicoloApiConstants.NON_TROVATO, id);
throw new LedaNotFoundException(ErroreEnum.FASCICOLO_NON_TROVATO, FascicoloApiConstants.FASCICOLO_CON_ID + " " + id + FascicoloApiConstants.NON_TROVATO);
}
var fascicoloEntity = optionalFascicoloEntity.get();
LedaUtils.checkUpdatedId(id, fascicoloEntity.getId(), FascicoloApiConstants.ID_RISORSA_AGGIORNATA);
if (StatoEnum.BOZZA.equals(fascicoloEntity.getStato())) {
isFirstSave = true;
}
fascicoloMapper.updateFromDto(fascicolo, fascicoloEntity);
fascicoloEntity.setStato(StatoEnum.COMPLETO);
//Generazione numero fascicolo
var count = fascicoloRepository.getMaxCounter();
if (count == null) {
count = 1L;
fascicoloEntity.setCounterNumeroFascicolo(count);
} else {
count++;
fascicoloEntity.setCounterNumeroFascicolo(count);
}
String numeroFascicolo = "" + fascicoloEntity.getAnnoDiCompetenza() + count + fascicoloEntity.getUnitaOrganizzativa();
fascicoloEntity.setNumeroFascicolo(numeroFascicolo);
var fascicoloSalvato = fascicoloRepository.save(fascicoloEntity);
//Se il fascicolo passa in stato COMPLETO per la prima volta allora creo il ricorso
if (isFirstSave)
ricorsoService.createRicorso(fascicoloSalvato.getId());
log.info(FascicoloApiConstants.FASCICOLO_CON_ID + " {} " + FascicoloApiConstants.AGGIORNATO, id);
return fascicoloMapper.toApiModel(fascicoloSalvato);
}
The method where the code stops is the fascicoloRepository.getMaxCounter()
Here is the createRicorso method of the RicorsoService class:
#Transactional(rollbackFor = {Exception.class})
public RicorsoDTO createRicorso(Long idFascicolo) throws LedaException {
LedaUtils.checkNull(idFascicolo, "id risorsa");
var fascicoloENT = fascicoloRepository.getReferenceById(idFascicolo);
var ricorsoENT = new RicorsoENT();
ricorsoENT.setFascicolo(fascicoloENT);
ricorsoENT.setStato(StatoRicorsoEnum.APERTO);
return ricorsoMapper.toApiModelRicorso(ricorsoRepository.save(ricorsoENT));
}
Operations on FascicoloENT are not cascaded on RicorsoENT. There is no cascade type.
This is the query taken from logs where the code stops:
Hibernate:
update
tb_fascicoli
set
row_updated_dttm=?,
row_created_dttm=?,
row_updated_user=?,
row_created_user=?,
anno_competenza=?,
count_numero_fascicolo=?,
data_apertura=?,
flag_unito=?,
id_motivo_apertura=?,
id_motivo_arichiviazione=?,
note=?,
numero_fascicolo=?,
numero_fascicolo_unito=?,
numero_ricorrenti=?,
fk_ricorrente_principale=?,
id_sede_tar=?,
stato=?,
step=?,
terzo_interessato=?,
tipologia_ricorso=?,
unita_organizzativa=?
where
id=?
I tried deliting #Transactional on CreateRicorso method but it still doesn't work.
I think the problem is a metter of transactions or threads...
Can anyone help me understand why the application gets stuck and how to solve?
PS: let me know if you need extra information
Many thanks,
Saverio
Related
I checked to see if the keyspace exists before creation, but both checks returns null. However, when I execute the keyspace creation cql file, it always returns that the keyspace already exists. I'm using org.cassandra:cassandra:cassandra-unit-spring:3.1.3.2 package. Not sure how to fix this issue. I have another method, '''EmbeddedCassandraServerHelper.cleanEmbeddedCassandra()''', that seemingly drops the keyspace since it also returns null when checking for library keyspace. So at this point, I'm assuming its a logic error on my part but I can't figure it out.
public static void setupEmbeddedCassandra(String cqlFile, String keyspaceName)
throws IOException, TTransportException, InterruptedException {
synchronized (lock) {
if (finalKeyspace == null) {
System.out.println(CLASS_NAME + " Setting up local embedded cassandra environment");
EmbeddedCassandraServerHelper.startEmbeddedCassandra();
int embeddedCassandraPort = EmbeddedCassandraServerHelper.getNativeTransportPort();
System.setProperty("embeddedCassandraPort", String.valueOf(embeddedCassandraPort));
cluster =
(new Cluster.Builder())
.addContactPoints(new String[] {"localhost"})
.withPort(embeddedCassandraPort)
.build();
session = cluster.connect();
cluster.getConfiguration();
System.out.println(cluster.getMetadata().getKeyspace("library"));
CQLDataLoader dataLoader = new CQLDataLoader(session);
System.out.println(cluster.getMetadata().getKeyspace("library"));
dataLoader.load(new ClassPathCQLDataSet(cqlFile, true, keyspaceName));
System.out.println(
CLASS_NAME
+ " Embedded Server started on port "
+ EmbeddedCassandraServerHelper.getNativeTransportPort());
}
finalKeyspace = keyspaceName;
}
}
I downloaded a lot of blockchain data using https://bitcoin.org, I took some file and I try to analyse it with bitcoinj library.
I would like to get information from every transaction:
-who send bitcoins,
-how much,
-who receive bitcoins.
I use:
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-core</artifactId>
<version>0.15.10</version>
</dependency>
I have a code:
NetworkParameters np = new MainNetParams();
Context.getOrCreate(MainNetParams.get());
BlockFileLoader loader = new BlockFileLoader(np,List.of(new File("test/resources/blk00450.dat")));
for (Block block : loader) {
for (Transaction tx : block.getTransactions()) {
System.out.println("Transaction ID" + tx.getTxId().toString());
for (TransactionInput ti : tx.getInputs()) {
// how to get wallet addresses of inputs?
}
// this code works for 99% of transactions but for some throws exceptions
for (TransactionOutput to : tx.getOutputs()) {
// sometimes this line throws: org.bitcoinj.script.ScriptException: Cannot cast this script to an address
System.out.println("out address:" + to.getScriptPubKey().getToAddress(np));
System.out.println("out value:" + to.getValue().toString());
}
}
}
Can you share some snippet that will work for all transactions in the blockchain?
There are at least two type of transaction, P2PKH and P2SH.
Your code would work well with P2PKH, but wouldn not work with P2SH.
You can change the line from:
System.out.println("out address:" + to.getScriptPubKey().getToAddress(np));
to:
System.out.println("out address:" + to.getAddressFromP2PKHScript(np)!=null?to.getAddressFromP2PKHScript(np):to.getAddressFromP2SH(np));
The API of Bitcoin says the methods getAddressFromP2PKHScript() and getAddressFromP2SH() are deprecated, and I have not find suitable method.
However, P2SH means "Pay to Script Hash", which means it could contain two or more public keys to support multi-signature. Moreover, getAddressFromP2SH() returns only one address, perhaps this is the reason why it is deprecated.
I also wrote a convinient method to check the inputs and outputs of a block:
private void printCoinValueInOut(Block block) {
Coin blockInputSum = Coin.ZERO;
Coin blockOutputSum = Coin.ZERO;
System.out.println("--------------------Block["+block.getHashAsString()+"]------"+block.getPrevBlockHash()+"------------------------");
for(Transaction tx : block.getTransactions()) {
Coin txInputSum = tx.getOutputSum();
Coin txOutputSum = tx.getOutputSum();
blockInputSum = blockInputSum.add(txInputSum);
blockOutputSum = blockOutputSum.add(txOutputSum);
System.out.println("Tx["+tx.getTxId()+"]:\t" + txInputSum + "(satoshi) IN, " + txOutputSum + "(satoshi) OUT.");
}
System.out.println("Block total:\t" + blockInputSum + "(satoshi) IN, " + blockOutputSum + "(satoshi) OUT. \n");
}
When I try to find out the value that is not there in the database I get 500 Internal Server Error. I have already provided logic to throw ResourceNotFoundException error, but, it's not working for some reason. What do I need to do to get 404 ResourceNotFoundException instead of 500 Internal Server Error.
Here's my code:
#PostMapping("/start/{id}")
public ResponseEntity<String> startEvent(#PathVariable() Long id) {
Event event = this.eventRepository.findById(id).get();
if (event == null) {
throw new ResourceNotFoundException("Event with id " + id + " not found.");
}
event.setStarted(true);
this.eventRepository.save(event);
return ResponseEntity.ok("Event " + event.getName() + " has started");
}
I guess eventRepository.findById(id) //id = 200 returns 500 response as record with id 200 does not exist in the database. What should I do to get ResourceNotFoundException?
eventRepository.findById returns Optional (in Spring Data JPA 2.0.6, see https://docs.spring.io/spring-data/jpa/docs/2.0.6.RELEASE/reference/html/#repositories.core-concepts)
Optional.get on empty optional causes NoSuchElementException (https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#get--). Your if (event == null) comes too late.
Checking stactrace, you should see that exception comes from line with this.eventRepository.findById and actual exception is NoSuchElementException
To fix that you should change your code to
Optional<Event> optionalEvent= this.eventRepository.findById(id);
if (!optionalEvent.isPresent()) {
throw new ResourceNotFoundException("Event with id " + id + " not found.");
}
Event event=optionalEvent.get();
//the rest of your logic
You may also write your code in more functional way
Event event = this.eventRepository
.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Event with id " + id + " not found."))
Summary
Do not call get() on Optional without checking if it is present (using isPresent() method)
eventRepository.findById() return an Optional
Therefore you have to test for existence before get()
Optional<Event> optEvent = eventRepository.findById();
if (!optEvent.isPresent()) {
//throw exception here
}
I am really new to SOAP web services and to Netsuite ERP and I am trying to generate a report in my company where I need to obtain all the Clients and their Invoices using the data available in Netsuite ERP. I followed the Java and Axis tutorial they offer with their sample app for the ERP and I successfully created a Java project in Eclipse that consumes the WSDL for netsuite 2015-2 and compiles the needed classes to run the sample app. So, I followed an example found in their CRM exapmle app to obtain a Client's information but the only problem is that their example method needs you to introduce the Client's ID. Here is the sample code:
public int getCustomerList() throws RemoteException,
ExceededUsageLimitFault, UnexpectedErrorFault, InvalidSessionFault,
ExceededRecordCountFault, UnsupportedEncodingException {
// This operation requires a valid session
this.login(true);
// Prompt for list of internalIds and put in an array
_console
.write("\ninternalIds for records to retrieved (separated by commas): ");
String reqKeys = _console.readLn();
String[] internalIds = reqKeys.split(",");
return getCustomerList(internalIds, false);
}
private int getCustomerList(String[] internalIds, boolean isExternal)
throws RemoteException, ExceededUsageLimitFault,
UnexpectedErrorFault, InvalidSessionFault, ExceededRecordCountFault {
// Build an array of RecordRef objects and invoke the getList()
// operation to retrieve these records
RecordRef[] recordRefs = new RecordRef[internalIds.length];
for (int i = 0; i < internalIds.length; i++) {
RecordRef recordRef = new RecordRef();
recordRef.setInternalId(internalIds[i]);
recordRefs[i] = recordRef;
recordRefs[i].setType(RecordType.customer);
}
// Invoke getList() operation
ReadResponseList getResponseList = _port.getList(recordRefs);
// Process response from get() operation
if (!isExternal)
_console.info("\nRecords returned from getList() operation: \n");
int numRecords = 0;
ReadResponse[] getResponses = getResponseList.getReadResponse();
for (int i = 0; i < getResponses.length; i++) {
_console.info("\n Record[" + i + "]: ");
if (!getResponses[i].getStatus().isIsSuccess()) {
_console.errorForRecord(getStatusDetails(getResponses[i]
.getStatus()));
} else {
numRecords++;
Customer customer = (Customer) getResponses[i].getRecord();
_console.info(" internalId="
+ customer.getInternalId()
+ "\n entityId="
+ customer.getEntityId()
+ (customer.getCompanyName() == null ? ""
: ("\n companyName=" + customer
.getCompanyName()))
+ (customer.getEntityStatus() == null ? ""
: ("\n status=" + customer.getEntityStatus().getName()))
+ (customer.getEmail() == null ? ""
: ("\n email=" + customer.getEmail()))
+ (customer.getPhone() == null ? ""
: ("\n phone=" + customer.getPhone()))
+ "\n isInactive="
+ customer.getIsInactive()
+ (customer.getDateCreated() != null ? ""
: ("\n dateCreated=" + customer
.getDateCreated().toString())));
}
}
return numRecords;
}
So as you can see, this method needs the internal ID of each Customer which I find not useful as I have a many Customers and I don't want to pass each Customer's ID. I read their API docs (which I find hard to navigate and kind of useless) and I found a web service called getAll() that gives all the records given a getAllRecord object which requires a getAllRecordType object. However, the getAllRecordType object does not support Customer entities, so I can't obtain all the customers on the ERP this way.
Is there an easy way to obtain all the Customers in my Netsuite ERP (maybe using other thing rather than the SOAP Web Services they offer? I am desperate about this situation as understanding how Netsuite's Web Services API has been really troublesome.
Thanks!
You would normally use a search to select a list of customers. On a large account you would not normally get all customers on any regular basis. If you are trying to get the invoices you might just find it more practical to get those with a search.
You wrote "in your company". Are you trying to write an application of some sort? If this is an internal project (and even if it's not) you'll probably find using SuiteScripts much more efficient in terms of your time and frustration level.
I made it using the following code on my getCustomerList method:
CustomerSearch customerSrch = new CustomerSearch();
SearchResult searchResult = _port.search(customerSrch);
System.out.println(searchResult.getTotalRecords());
RecordList rl = searchResult.getRecordList();
for (int i = 0; i <searchResult.getTotalRecords()-1; i++) {
Record r = rl.getRecord(i);
System.out.println("Customer # " + i);
Customer testcust = (Customer)r;
System.out.println("First Name: " + testcust.getFirstName());
}
I have a OneToOne relationship between two tables, as shown below:
PreRecordLoad.java:
#OneToOne(mappedBy="preRecordLoadId",cascade = CascadeType.ALL)
private PreRecordLoadAux preRecordLoadAux;
PreRecordLoadAux.java:
#JoinColumn(name = "PRE_RECORD_LOAD_ID", referencedColumnName = "PRE_RECORD_LOAD_ID")
#OneToOne
private PreRecordLoad preRecordLoadId;
I'm using this method to pull back the PreRecordLoad object:
public PreRecordLoad FindPreRecordLoad(Long ID)
{
print("Finding " + ID + "f");
Query query;
PreRecordLoad result = null;
try
{
query = em.createNamedQuery("PreRecordLoad.findByPreRecordLoadId");
query.setParameter("preRecordLoadId", ID);
result = (PreRecordLoad)query.getSingleResult();
//result = em.find(PreRecordLoad.class, ID);
}
catch(Exception e)
{
print(e.getLocalizedMessage());
}
return result;
}
The '+ "f"' is to see if the passed value somehow had something at the end. It didn't.
I originally used em.find, but the same issue occurred no matter which method I used.
I used to use a BigDecimal for the ID because it was the default, and noticed I was getting a precision difference when it worked, and when it didn't work. Specifically the precision was 4 when it didn't work, but 0 when it did. I couldn't work out why this was, so I changed the BigDecimal to a Long, as I never really needed it to be a BigDecimal anyway.
When I save the new PreRecordLoad and PreRecordLoadAux objects to the database (inserting them for the first time), and then try and run this method to recall the objects, it retrieves the PreRecordLoad, but the PreRecordLoadAux is null. This is despite the entry being in the database and what looks to be full committed, as I can access it from SQLDeveloper, which is a separate session.
However, if I stop and re-run the application, then it successfully pulls back both objects. The ID being passed is the same both times, or at least appears to be.
Anyway suggestions would be greatly appreciated, thankyou.
Edit:
Here is the code for when I am persisting the objects into the DB:
if(existingPreAux==null) {
try {
preLoad.setAuditSubLoadId(auditLoad);
em.persist(preLoad);
print("Pre Record Load entry Created");
preAux.setPreRecordLoadId(preLoad);
em.persist(preAux);
print("Pre Record Load Aux entry Created");
}
catch(ConstraintViolationException e) {
for(ConstraintViolation c : e.getConstraintViolations()) {
System.out.println (c.getPropertyPath() + " " + c.getMessage());
}
}
}
else {
try {
preLoad.setPreRecordLoadId(existingPreLoad.getPreRecordLoadId());
preLoad.setAuditSubLoadId(auditLoad);
em.merge(preLoad);
print("Pre Record Load entry found and updated");
preAux.setPreRecordLoadAuxId(existingPreAux.getPreRecordLoadAuxId());
preAux.setPreRecordLoadId(preLoad);
em.merge(preAux);
print("Pre Record Load Aux entry found and updated");
}
catch(ConstraintViolationException e) {
for(ConstraintViolation c : e.getConstraintViolations()) {
System.out.println (c.getPropertyPath() + " " + c.getMessage());
}
}
}
That's in a method, and after that code, the method ends.
It's your responsibility to maintain the coherence of the object graph. So, when you do preAux.setPreRecordLoadId(preLoad);, yo must also do preLoad.setPreRecordLoadAux(preAux);.
If you don't, then every time you'll load the preAux from the same session, it will be retrieved from the first-level cache, and will thus return your incorrectly initialized instance of the entity.