Call transactional method Play Java JPA Hibernate - java

I have 2 database one is mysql and other is postgree.
I tried to get postgree data from mysql transactional method.
#Transactional(value = "pg")
public List<String> getSubordinate(){
Query q1 = JPA.em().createNativeQuery("select vrs.subordinate_number, vrs.superior_number\n" +
"from view_reporting_structure vrs\n" +
"where vrs.superior_number = :personel_number");
q1.setParameter("personel_number","524261");
List<String> me = q1.getResultList();
return me;
}
}
from another method
#Transactional
public Result getOpenRequestList(){
Subordinate subordinate = new Subordinate();
List<String> subordinateData = subordinate.getSubordinate();
....
}
i got error
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Table 'db_hcm.view_reporting_structure' doesn't exist
so my Postgre method recognized as mySQL transaction which is the view not exist in mySQL database. how do I get data from different presistence unit with 1 method?

I never did it (different databases), but I guess the following may work.
For example, you have the following data source definition in application.conf:
# MySql
db.mysql.driver=com.mysql.jdbc.Driver
... the rest of setting for db.mysql
# H2
db.postgre.driver=org.postgresql.Driver
... the rest of setting for db.postgre
Instead of using #Transactional annotation, manage a transaction explicitly and use JPA withTransaction API:
private static final String MYSQL_DB = "mysql";
private static final String POSTGRE_DB = "postgre";
public List<String> getSubordinate() {
JPA.withTransaction(MYSQL_DB, true/* this is read-only flag*/,
() -> {
Query q1 = JPA.em().createNativeQuery("select vrs.subordinate_number, vrs.superior_number\n" +
"from view_reporting_structure vrs\n" +
"where vrs.superior_number = :personel_number");
q1.setParameter("personel_number","524261");
List<String> me = q1.getResultList();
return me;
}
}
public Result getOpenRequestList(){
JPA.withTransaction(POSTGRE_DB, true/* this is read-only flag*/,
() -> {
Subordinate subordinate = new Subordinate();
List<String> subordinateData = subordinate.getSubordinate();
....
}
}
Note: I prefer always use withTransaction, since it allows better control of unhappy flow. You should wrap the call with try-catch. If JPA throws a run-time exception on commit, you can do proper error handling. In case of using #Transactional annotation, commit takes place after controller have finished and you cannot handle the error.

Related

What are the solutions for findByN1QL in SDK3?

When migrating from Couchbase SDK 2 to SDK 3 certain document formats seem to have been removed.
How can this format or an alternative output be used in Couchbase SDK 3 to handle the below-indicated API change?
This is one of the sample classes that used findByN1QL in the existing system.
private List<Document> getBspReconciledAgentTransactionDataList(
BspReconciliationAgentTransactionLogicData transactionLogicData) {
final String bucketName = getBucketName(repository);
String query = getTransactionQueryStatement(transactionLogicData).toString();
query = query.split(N1qlQueryUtil.WHERE)[NumberConstants.ONE];
query = N1qlQueryUtil.selectOf(N1qlQueryUtil.metaOf(bucketName, "id", "_ID"),
N1qlQueryUtil.metaOf(bucketName, "cas", "_CAS"), N1QlQueryConstants.COUNTRY_NAME,
N1QlQueryConstants.COUNTRYCODE, N1QlQueryConstants.AIRLINECODE, N1QlQueryConstants.TRANSACTIONTYPE,
N1QlQueryConstants.SUBMISSIONSTATUS, N1QlQueryConstants.RECONCILIATIONSTATUS,
N1QlQueryConstants.IATA_CODE_CONST, N1QlQueryConstants.AGENT_CODE_CONST,
N1QlQueryConstants.TRANSACTION_DATE_CONST, N1QlQueryConstants.DPC_PROCESSING_DATE,
N1QlQueryConstants.E_TICKET_NO, N1QlQueryConstants.ORDER_ID, N1QlQueryConstants.PASSENGER_NAME,
N1QlQueryConstants.CURRENCY, N1QlQueryConstants.DEBIT_AMOUNT_POSTED,
N1QlQueryConstants.CREDIT_AMOUNT_POSTED, N1QlQueryConstants.DEBIT_AMOUNT_FROM_HOT_FILE,
N1QlQueryConstants.CREDIT_AMOUNT_FROM_HOT_FILE, N1QlQueryConstants.ACMADM_REF_ID,
N1QlQueryConstants.DOCUMENT_ID) +
N1qlQueryUtil.fromOf(bucketName)
+ N1qlQueryUtil.whereOf(N1QlQueryConstants.CLASS_DETAIL_DOCUMENT + query);
return getCouchbaseOperations(repository).findByN1QL(couchbaseConfiguration.cluster().query(query),
BSPReconciledDetailDocument.class);
private <T, I extends Serializable> CouchbaseOperations getCouchbaseOperations(
CouchbaseRepository<T, I> repository) {
return repository.getOperations();
}
Error showing line "findByN1QL",
return getCouchbaseOperations(repository).findByN1QL(couchbaseConfiguration.cluster().query(query),
BSPReconciledDetailDocument.class);
What would be the best possible options?
I have a solution to "findByN1QL" using cluster,
// return getCouchbaseOperations(repository).findByN1QL(N1qlQuery.simple(query),
// BSPReconciledDetailDocument.class);
return cluster.query(query).rowsAs(BSPReconciledDetailDocument.class);
With this scenario not used "CouchbaseRepository".Used Cluster.
#Autowired
private Cluster cluster;
private List<Document> getBspReconciledAgentTransactionDataList(
BspReconciliationAgentTransactionLogicData transactionLogicData) {
final String bucketName = getBucketName(repository);
String query = getTransactionQueryStatement(transactionLogicData).toString();
query = query.split(N1qlQueryUtil.WHERE)[NumberConstants.ONE];
query = N1qlQueryUtil.selectOf(N1qlQueryUtil.metaOf(bucketName, "id", "_ID"),
N1qlQueryUtil.metaOf(bucketName, "cas", "_CAS"), N1QlQueryConstants.COUNTRY_NAME,
N1QlQueryConstants.COUNTRYCODE, N1QlQueryConstants.AIRLINECODE, N1QlQueryConstants.TRANSACTIONTYPE,
N1QlQueryConstants.SUBMISSIONSTATUS, N1QlQueryConstants.RECONCILIATIONSTATUS,
N1QlQueryConstants.IATA_CODE_CONST, N1QlQueryConstants.AGENT_CODE_CONST,
N1QlQueryConstants.TRANSACTION_DATE_CONST, N1QlQueryConstants.DPC_PROCESSING_DATE,
N1QlQueryConstants.E_TICKET_NO, N1QlQueryConstants.ORDER_ID, N1QlQueryConstants.PASSENGER_NAME,
N1QlQueryConstants.CURRENCY, N1QlQueryConstants.DEBIT_AMOUNT_POSTED,
N1QlQueryConstants.CREDIT_AMOUNT_POSTED, N1QlQueryConstants.DEBIT_AMOUNT_FROM_HOT_FILE,
N1QlQueryConstants.CREDIT_AMOUNT_FROM_HOT_FILE, N1QlQueryConstants.ACMADM_REF_ID,
N1QlQueryConstants.DOCUMENT_ID) +
N1qlQueryUtil.fromOf(bucketName)
+ N1qlQueryUtil.whereOf(N1QlQueryConstants.CLASS_DETAIL_DOCUMENT + query);
return cluster.query(query).rowsAs(BSPReconciledDetailDocument.class);
Does it work or not? Does anyone have any idea about it?
You can use the SPEL expression in an #Query method in a repository interfaces as shown below (assuming the n1ql statement will return Persons).
#Query("#{[0]}")
List<Person> myN1ql(String n1qlString);

Is there a spring for graphql alternative for netflix's #DGSDataloader and DGSData

Trying to migrate a Netflix DGS GraphQL and Neo4J project to now use Spring for GraphQL and Neo4J instead. Hit a roadblock when I wanted to avoid the N+1 problem.
Yes, there is an alternative to avoid the N+1 problem in Spring for GraphQL. It's the #BatchMapping annotation:
Suppose you have the following schema:
type Query {
artistas: [Artista]
}
type Artista {
id: ID
apellido: String
estilo: String
obras:[Obra]
}
type Obra{
artistaId: ID
titulo: String
imagen: String
}
And the following #QueryMapping:
#QueryMapping
Flux<Artista> artistas(){
return Flux.fromIterable(allArtistas);
}
Our Artista DTO contains a List of Obra we may sometimes want, so it can cause us the N+1 problem:
record Artista (Long id, String apellido, String estilo, List<Obra> obras){}
record Obra (Long artistaId, String titulo, String imagen){}
So if you add an additional mapping method annotated with #BatchMapping, you tell the GraphQL engine to fetch that data using a DataLoader under the hood and keeping it at hand for each DB roundtrip, for example.
#BatchMapping(typeName = "Artista")
Mono<Map<Artista, List<Obra>>> obras(List<Artista> artistas){
var artistasIds = artistas.stream()
.map(Artista::id)
.toList();
var todasLasObras = obtenerObras(artistasIds);
return todasLasObras.collectList()
.map(obras -> {
Map<Long, List<Obra>> obrasDeCadaArtistaId = obras.stream()
.collect(Collectors.groupingBy(Obra::artistaId));
return artistas.stream()
.collect(Collectors.toMap(
unArtista -> unArtista, //K, el Artista
unArtista -> obrasDeCadaArtistaId.get(Long.parseLong(unArtista.id().toString())))); //V, la lista de obras
});
}
private Flux<Obra> obtenerObras(List<Long> artistasIds) {
// ...your service-specific way of getting all the Obras from each artistaId...
}
If you throw some logs here and there you can check it only fetches the Obras once.
Hope it helps!

Flexible search with parameters return null value

I have to do this flexible search query in a service Java class:
select sum({oe:totalPrice})
from {Order as or join CustomerOrderStatus as os on {or:CustomerOrderStatus}={os:pk}
join OrderEntry as oe on {or.pk}={oe.order}}
where {or:versionID} is null and {or:orderType} in (8796093066999)
and {or:company} in (8796093710341)
and {or:pointOfSale} in (8796097413125)
and {oe:ecCode} in ('13','14')
and {or:yearSeason} in (8796093066981)
and {os:code} not in ('CANCELED', 'NOT_APPROVED')
When I perform this query in the hybris administration console I correctly obtain:
1164.00000000
In my Java service class I wrote this:
private BigDecimal findGroupedOrdersData(String total, String uncDisc, String orderPromo,
Map<String, Object> queryParameters) {
BigDecimal aggregatedValue = new BigDecimal(0);
final StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("select sum({oe:").append(total).append("})");
queryBuilder.append(
" from {Order as or join CustomerOrderStatus as os on {or:CustomerOrderStatus}={os:pk} join OrderEntry as oe on {or.pk}={oe.order}}");
queryBuilder.append(" where {or:versionID} is null");
if (queryParameters != null && !queryParameters.isEmpty()) {
appendWhereClausesToBuilder(queryBuilder, queryParameters);
}
queryBuilder.append(" and {os:code} not in ('");
queryBuilder.append(CustomerOrderStatus.CANCELED.getCode()).append("', ");
queryBuilder.append("'").append(CustomerOrderStatus.NOT_APPROVED.getCode()).append("')");
FlexibleSearchQuery query = new FlexibleSearchQuery(queryBuilder.toString(), queryParameters);
List<BigDecimal> result = Lists.newArrayList();
query.setResultClassList(Arrays.asList(BigDecimal.class));
result = getFlexibleSearchService().<BigDecimal> search(query).getResult();
if (!result.isEmpty() && result.get(0) != null) {
aggregatedValue = result.get(0);
}
return aggregatedValue;
}
private void appendWhereClausesToBuilder(StringBuilder builder, Map<String, Object> params) {
if ((params == null) || (params.isEmpty()))
return;
for (String paramName : params.keySet()) {
builder.append(" and ");
if (paramName.equalsIgnoreCase("exitCollection")) {
builder.append("{oe:ecCode}").append(" in (?").append(paramName).append(")");
} else {
builder.append("{or:").append(paramName).append("}").append(" in (?").append(paramName).append(")");
}
}
}
The query string before the search(query).getResult() function is:
query: [select sum({oe:totalPrice}) from {Order as or join CustomerOrderStatus as os on {or:CustomerOrderStatus}={os:pk}
join OrderEntry as oe on {or.pk}={oe.order}} where {or:versionID} is null
and {or:orderType} in (?orderType) and {or:company} in (?company)
and {or:pointOfSale} in (?pointOfSale) and {oe:ecCode} in (?exitCollection)
and {or:yearSeason} in (?yearSeason) and {os:code} not in ('CANCELED', 'NOT_APPROVED')],
query parameters: [{orderType=OrderTypeModel (8796093230839),
pointOfSale=B2BUnitModel (8796097413125), company=CompanyModel (8796093710341),
exitCollection=[13, 14], yearSeason=YearSeasonModel (8796093066981)}]
but after the search(query) result is [null].
Why? Where I wrong in the Java code? Thanks.
In addition, if you want to disable restriction in your java code. You can do like this ..
#Autowired
private SearchRestrictionService searchRestrictionService;
private BigDecimal findGroupedOrdersData(String total, String uncDisc, String orderPromo,
Map<String, Object> queryParameters) {
searchRestrictionService.disableSearchRestrictions();
// You code here
searchRestrictionService.enableSearchRestrictions();
return aggregatedValue;
}
In the above code, You can disabled the search restriction and after the search result, you can again enable it.
OR
You can use sessionService to execute flexible search query in Local View. The method executeInLocalView can be used to execute code within an isolated session.
(SearchResult<? extends ItemModel>) sessionService.executeInLocalView(new SessionExecutionBody()
{
#Override
public Object execute()
{
sessionService.setAttribute(FlexibleSearch.DISABLE_RESTRICTIONS, Boolean.TRUE);
return flexibleSearchService.search(query);
}
});
Here you are setting DISABLE RESTRICTIONS = true which will run the query in admin context [Without Restriction].
Check this
Better i would suggest you to check what restriction exactly applying to your item type. You can simply check in Backoffice/HMC
Backoffice :
Go to System-> Personalization (SearchRestricion)
Search by Restricted Type
Check Filter Query and analysis your item data based on that.
You can also check its Principal (UserGroup) on which restriction applied.
To confirm, just check by disabling active flag.
Query in the code is running not under admin user (most likely).
And in this case the different search Restrictions are applied to the query.
You can see that the original query is changed:
start DB logging (/hac -> Monitoring -> Database -> JDBC logging);
run the query from the code;
stop DB logging and check log file.
More information: https://wiki.hybris.com/display/release5/Restrictions
In /hac console the admin user is usually used and restrictions will not be applied because of this.
As the statement looks ok to me i'm going to go with visibility of the data. Are you able to see all the items as whatever user you are running the query as? In the hac you would be admin obviously.

How to get fully materialized query from querydsl

I am trying to use querydsl for building dynamic queries for dynamic schemas. I am trying to get just the query instead of having to actually execute it.
So far I have faced two issues:
- The schema.table notation is absent. Instead I only get the table name.
- I have been able to get the query but it separates out the variables and puts '?' instead which is understandable. But I am wondering if there is some way to get fully materialized query including the parameters.
Here is my current attempt and result(I am using MySQLTemplates to create the configuration):
private SQLTemplates templates = new MySQLTemplates();
private Configuration configuration = new Configuration(templates);
String table = "sometable"
Path<Object> userPath = new PathImpl<Object>(Object.class, table);
StringPath usernamePath = Expressions.stringPath(userPath, "username");
NumberPath<Long> idPath = Expressions.numberPath(Long.class, userPath, "id");
SQLQuery sqlQuery = new SQLQuery(connection, configuration)
.from(userPath).where(idPath.eq(1l)).limit(10);
String query = sqlQuery.getSQL(usernamePath).getSQL();
return query;
And what I get is:
select sometable.username
from sometable
where sometable.id = ?
limit ?
What I wanted to get was:
select sometable.username
from someschema.sometable
where sometable.id = ?
limit ?
Update: I came up with this sort of hack to get parameters materialized(Not ideal and would love better solution) But still could not get Schema.Table notation to work:
Hack follows. Please suggest cleaner QueryDsl way of doing it:
String query = cleanQuery(sqlQuery.getSQL(usernamePath));
private String cleanQuery(SQLBindings bindings){
String query = bindings.getSQL();
for (Object binding : bindings.getBindings()) {
query = query.replaceFirst("\\?", binding.toString());
}
return query;
}
To enable schema printing use the following pattern
SQLTemplates templates = MySQLTemplates.builder()
.printSchema()
.build();
SQLTemplates subclasses were used before, but since some time the builder pattern is the official way to customize the templates http://www.querydsl.com/static/querydsl/3.3.1/reference/html/ch02s03.html#d0e904
And to enable direct serialization of literals use
//configuration level
configuration.setUseLiterals(true);
//query level
configuration.setUseLiterals(true);
Here is a full example
// configuration
SQLTemplates templates = MySQLTemplates.builder()
.printSchema()
.build();
Configuration configuration = new Configuration(templates);
// querying
SQLQuery sqlQuery = new SQLQuery(connection, configuration)
.from(userPath).where(idPath.eq(1l)).limit(10);
sqlQuery.setUseLiterals(true);
String query = sqlQuery.getSQL(usernamePath).getSQL();
If you always just want the SQL query string out, move setUseLiterals from query to configuration.
Concerning the usage of Querydsl expressions the usage of code generation like documented here is advised http://www.querydsl.com/static/querydsl/3.3.1/reference/html/ch02s03.html
It will make your code typesafe, compact and readable.
If you want to try Querydsl without code generation you can replace
Path<Object> userPath = new PathImpl<Object>(Object.class, variable);
with
Path<Object> userPath = new RelationalPathBase<Object>(Object.class, variable, schema, table);
When working with QueryDSL, you must provide a template for the database platform to build the query for. I see you are already are doing this here:
private SQLTemplates templates = new MySQLTemplates();
private Configuration configuration = new Configuration(templates);
To make the schema name appear in the generated query, the only way I have found to do this is (there may be an easier way) is to extend the template class and explicitly call this.setPrintSchema(true); inside the constructor. Here is a class that should work for MySql:
import com.mysema.query.sql.MySQLTemplates;
public class NewMySqlTemplates extends MySQLTemplates {
public NewMySqlTemplates() {
super('\\', false);
}
public NewMySqlTemplates(boolean quote) {
super('\\', quote);
}
public NewMySqlTemplates(char escape, boolean quote) {
super(escape, quote);
this.setPrintSchema(true);
}
}
Then simply use this NewMySqlTemplates class in place of the MySQLTemplates class like this:
private SQLTemplates templates = new NewMySQLTemplates();
private Configuration configuration = new Configuration(templates);
I have this working using PostgresTemplates, so I may have a typo or mistake in the NewMySqlTemplates class above, but you should be able to get it to work. Good luck!

how to use sqlsession to update mysql

I want to update the mysql database in spring mybatis. I have couple of mapper interface and want to perform update using sqlsession.commit(). How can I define each of sqlsession?
#Transactional
public BookingRecord book(Person person, Hotel hotel, String id) {
List<Room> rooms = hotel.getRooms();
for (Room room : rooms) {
if (room.isAvailable()) {
if (person.getBalance() > room.getPrice()) {
// 开始订房
room.setAvailable(false); // 保留房间
person.setId(id);
person.setBalance(person.getBalance() - room.getPrice()); //转账
hotel.setBalance(hotel.getBalance() + room.getPrice()); //转账2
BookingRecord r = new BookingRecord(); //生成订单
r.setHotelName(hotel.getName()); //记录酒店名
r.setRoomNumber(room.getRoomNumber()); //记录房间号
r.setPersonId(person.getId()); //记录客人id
r.setStartDate(new Date());
personMapper.update(person);
brMapper.create(r);
hotelMapper.updateBalance(hotel);
roomMapper.update(room);
return r;
}
}
}
log.info(person.getName() + "successfully booked at " + hotel.getName());
return null;
}
#Transactional make this class transactional means spring will manage begin, commit and rollback procedure during executions. Transactions itself is a very wide area of research so I suggest you should read Spring Transaction to understand more about how Spring manages transaction. And in regards to your question, I suggest you should inject JDBC connection to this bean to perform begin, (commit or rollback) prophetically.

Categories

Resources