Spring Using SpEL Principal - java

I'm trying to use SpEL has in this document
https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions to do a query filtering results by the ?#{principal.id}
The problem is that Spring returns an exception
org.hibernate.QueryException: Not all named parameters have been set:
[1] [select p , p.store, p.category from Product p JOIN p.store s
JOIN p.category c WHERE p.store.id = :id AND p.keywords LIKE :keyword
AND p.store.ownerId = ?1 ]; nested exception is
java.lang.IllegalArgumentException: org.hibernate.QueryException: Not
all named parameters have been set: [1] [select p , p.store,
p.category from Product p JOIN p.store s JOIN p.category c WHERE
p.store.id = :id AND p.keywords LIKE :keyword AND p.store.ownerId = ?1
]
I have setup the following code and it is being executed.
#Service
public class SecurityEvaluationContextExtension extends EvaluationContextExtensionSupport {
#Override
public String getExtensionId() {
return "security";
}
#Override
public SecurityExpressionRoot getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println("SER >>>>> " + authentication.getPrincipal().toString() + " -- " );
return new SecurityExpressionRoot(authentication) {};
}
}
#Configuration
#EnableJpaRepositories
public class SecurityConfiguration {
#Bean
EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtension();
}
}
I'm using this server has a Spring Resource Server and so using the translation bellow to get the Id from the Authorization server. I confirm the code is executing and translating well but I'm getting the exception above.
#Service
public class myPrincipalExtractor implements PrincipalExtractor {
#Override
public UserInfo extractPrincipal(Map<String, Object> map) {
Map<String,Object> principal = null;
if (map.containsKey("principal")) {
principal = (Map<String, Object>) map.get("principal");
}
UserInfo user = new UserInfo();
if (principal != null ) {
if (principal.containsKey("id")) {
user.setId(Long.parseLong(principal.get("id").toString()));
}
if (principal.containsKey("username")) {
user.setUsername(principal.get("username").toString());
}
if (principal.containsKey("email")) {
user.setEmail(principal.get("email").toString());
}
}
System.out.println("----> " + user.getUsername() + " -> " + user.getId());
return user;
}
}
The query is...
#CrossOrigin
public interface StoreRepository extends CrudRepository<Store, Long>
{
#Query("select p , p.store, p.category from Product p JOIN p.store s " +
" JOIN p.category c " +
" WHERE p.store.id = :id AND p.keywords LIKE %:keyword% AND p.store.ownerId = ?#{principal.id} ")
List<Product> findByKeywordIgnoreCase(#Param("id") Long id , #Param("keyword") String keyword);
}
More Information:
I did this code bellow in the SecurityExpressionRoot so now I know this is really getting called when I place the SpEL and the ID and Username exist in the object. Also tried casting the returned object to Principal and the same problem happened.
AND p.store.ownerId = ?#{principal.id}
return new SecurityExpressionRoot(authentication) {
#Override
public UserInfo getPrincipal() {
System.out.println("Fetching the principal has user " + authentication.getPrincipal().toString());
return (UserInfo) authentication.getPrincipal();
}
};

In the website example (https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions) they specify you can use ?#{principal.id} and that really gets called when executing the code above but it fails on the #Query binding for some reason.
However I tried running it with another example case
#Query("select p , p.store, p.category from Product p JOIN p.store s " +
" JOIN p.category c " +
" WHERE p.store.id = :id AND p.keywords LIKE %:keyword% AND p.store.ownerId = ?#{#security.principal.id} ")
And this worked.
I found this other example here:
https://github.com/spring-projects/spring-data-examples/blob/master/jpa/security/src/main/java/example/springdata/jpa/security/SecureBusinessObjectRepository.java

Related

How do i check if record already exists use same record else create in java using spring boot

I want to check if user alraedy exits use same data else create new record
`
#EventHandler
#Override
public void on(ContentSaveUserEvent event) {
var existingRecord =classesCurriculumMapRepository.findByClassIdAndCurriculumMapId(Long.valueOf(event.getClassId()), event.getCurriculumMapId());
if (!existingRecord.isEmpty()) {
// here i want to use same record if already exits based on classid and curriculummapid
}
else {
Class_CurriculumMap classCurriculumMap= new Class_CurriculumMap();
classCurriculumMap.setId(new Class_CurriculumMapPK(Long.valueOf(event.getClassId()), event.getCurriculumMapId()));
classCurriculumMap.setDateLastModified(new Date());
classCurriculumMap.setUserLastModified(event.getUctx().getUserId());
classCurriculumMap.setStatus(Status.Active.value);
classesCurriculumMapRepository.save(classCurriculumMap);
}
}
}
`
the query i am using is:
`
#Query(value ="select * from class_curriculummap where ClassId =?1 And CurriculumMapId='?2'", nativeQuery = true)
List<Class_CurriculumMap> findByClassIdAndCurriculumMapId(Long classId, String curriculumMapId);
The response i am sending from postman is:
{
"classId": 1388126554733599886,
"curriculumMapId":"973BB040ggg16C44C4CA550FA14370499E2"
}
`
Replace your Query with below:
#Query(value ="select exists(select * from class_curriculummap where ClassId =?1 And CurriculumMapId='?2')", nativeQuery = true)
boolean isExists findByClassIdAndCurriculumMapId(Long classId, String curriculumMapId);
For more details, please refer to this article : Check if a row exists, otherwise insert

spring-data-mongodb #Update annotation with arrayFilters

spring-data-mongodb 3.4.0 have a beautiful #Update annotation support for jpa repositories.
But how can i pass arrayFilters and other options, supported by https://www.mongodb.com/docs/manual/reference/method/db.collection.update/ ?
Here is my working manual example:
public #NotNull Comment addComment(String id, String cid, String lid, String sid, Comment comment) {
// Add comment
JwtUser user = userService.getCurrentUser().orElseThrow(() ->
new SodixException("Can't get current user"));
EditorUser author = EditorUser.builder()
.id(user.getExternalId())
.username(user.getUsername())
.firstName(user.getFirstName())
.lastName(user.getLastName())
.build();
CommentEntity commentToSave = new CommentEntity();
commentToSave.setId(ProjectIdUtil.generateCommentId());
commentToSave.setCreatedAt(LocalDateTime.now());
commentToSave.setAuthor(author);
commentToSave.setMessage(comment.getMessage());
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(id));
query.addCriteria(Criteria.where("chapters").elemMatch(
Criteria.where("_id").is(cid).and("lessons").elemMatch(
Criteria.where("_id").is(lid).and("sections").elemMatch(
Criteria.where("_id").is(sid)
)
)
));
Update update = new Update();
update.addToSet("chapters.$[c].lessons.$[l].sections.$[s].comments", commentToSave);
update.filterArray("c._id", cid);
update.filterArray("l._id", lid);
update.filterArray("s._id", sid);
var result = mongoTemplate.updateFirst(query, update, ProjectEntity.class);
if (result.getModifiedCount() < 1) {
throw new RuntimeException("Comment wasn't added");
}
}
Here is what i expect to run using repository #Update annotation, but it's not working yet:
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.Update;
...
#Repository
public interface ProjectRepository extends MongoRepository<ProjectEntity, String> {
...
#Query("{ _id : ?0, chapters : {$elemMatch : {_id: ?1, lessons: {$elemMatch: {_id: ?2, 'sections._id': ?3}}}}}")
#Update("{$set : { 'chapters.$[c].lessons.$[l].sections.$[s].comments' : ?4 }, $arrayFilters: {'c._id': ?1, 'l._id': ?2, 's._id': ?3}}")
long addComment(String id, String cid, String lid, String sid, CommentEntity comment);
...
}
Error i've got is:
com.mongodb.MongoWriteException: Write operation error on server localhost:27017. Write error: WriteError{code=2, message='No array filter found for identifier 'c' in path 'chapters.$[c].lessons.$[l].sections.$[s].comments'', details={}}.
Thanks for the help!

Can't get results from flink SQL query

I'm facing a problem in which I don't get results from my query in Flink-SQL.
I have some informations stored in two Kafka Topics, I want to store them in two tables and perform a join between them in a streaming way.
These are my flink instructions :
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tableEnv = TableEnvironment.getTableEnvironment(env);
// configure Kafka consumer
Properties props = new Properties();
props.setProperty("bootstrap.servers", "localhost:9092"); // Broker default host:port
props.setProperty("group.id", "flink-consumer"); // Consumer group ID
FlinkKafkaConsumer011<Blocks> flinkBlocksConsumer = new FlinkKafkaConsumer011<>(args[0], new BlocksSchema(), props);
flinkBlocksConsumer.setStartFromEarliest();
FlinkKafkaConsumer011<Transactions> flinkTransactionsConsumer = new FlinkKafkaConsumer011<>(args[1], new TransactionsSchema(), props);
flinkTransactionsConsumer.setStartFromEarliest();
DataStream<Blocks> blocks = env.addSource(flinkBlocksConsumer);
DataStream<Transactions> transactions = env.addSource(flinkTransactionsConsumer);
tableEnv.registerDataStream("blocksTable", blocks);
tableEnv.registerDataStream("transactionsTable", transactions);
Here is my SQL query :
Table sqlResult
= tableEnv.sqlQuery(
"SELECT block_timestamp,count(tx_hash) " +
"FROM blocksTable " +
"JOIN transactionsTable " +
"ON blocksTable.block_hash=transactionsTable.tx_hash " +
"GROUP BY blocksTable.block_timestamp");
DataStream<Test> resultStream = tableEnv
.toRetractStream(sqlResult,Row.class)
.map(t -> {
Row r = t.f1;
String field2 = r.getField(0).toString();
long count = Long.valueOf(r.getField(1).toString());
return new Test(field2,count);
})
.returns(Test.class);
Then, I print the results :
resultStream.print();
But I don't get any answers, my program is stuck...
For the schema used for serialization and deserialization, here is my test class which stores the result of my query (two fields a string and a long for respectively the block_timestamp and the count) :
public class TestSchema implements DeserializationSchema<Test>, SerializationSchema<Test> {
#Override
public Test deserialize(byte[] message) throws IOException {
return Test.fromString(new String(message));
}
#Override
public boolean isEndOfStream(Test nextElement) {
return false;
}
#Override
public byte[] serialize(Test element) {
return element.toString().getBytes();
}
#Override
public TypeInformation<Test> getProducedType() {
return TypeInformation.of(Test.class);
}
}
This is the same principle for BlockSchema and TransactionsSchema classes.
Do you know why I can't get the result of my query ? Should I test with BatchExecutionEnvironment ?

Spring JDBC & MySQL - Getting EmptyResultDataAccessException when using queryForObject() but data exists

my code is:
public AppMetaDBO getAppMetaRecord() throws SQLException {
JdbcOperations jdbcOperations = new JdbcTemplate(dataSource);
String query = "SELECT * FROM " + TABLE_APP_META;
logger.config("Executing SQL query:[" + query + "]");
return jdbcOperations.queryForObject(query, new AppMetaRowMapper());
}
Where AppMetaRowMapper is:
public class AppMetaRowMapper implements org.springframework.jdbc.core.RowMapper<AppMetaDBO> {
#Override
public AppMetaDBO mapRow(ResultSet resultSet, int i) throws SQLException {
return new AppMetaDBO(resultSet.getDouble(COL_LAST_SUPPORTED_VER));
}
}
And AppMetaDBO is simply:
#Data
#AllArgsConstructor
public class AppMetaDBO implements Serializable {
private double last_supported_version;
}
This throws a EmptyResultDataAccessExceptioneven though the DB table looks like this:
It is important to note that the same server is able to write to the db with no issues, and even read from a different table with no issues.
The code that reads without issues is very similar and works fine:
UserDBO result = null;
try {
JdbcOperations jdbcOperations = new JdbcTemplate(dataSource);
String query = "SELECT *" + " FROM " + TABLE_USERS + " WHERE " + COL_UID + "=" + quote(uid);
logger.config("Executing SQL query:[" + query + "]");
result = jdbcOperations.queryForObject(query, new UserDboRowMapper());
} catch(EmptyResultDataAccessException ignored) {}
return result;
Also important to note that the db server is not on a remote machine. In the local machine these issues are non-existent.
Please help I have absolutely no idea what I am doing wrong.
Thanks in advance.

select all from a JPQL query, with paremeters

I have selection parameters on flat data, only don't know how to either omit a parameter entirely, or make it a complete wildcard. The search might use one or all parameters. How is this done? With ANY or ALL? Or, is there another way?
I would like to use one general query with all the paremeters, and pass in "all" or "any", something along those lines, for some of those parameters.
existing code:
package legacy.database;
import java.sql.Timestamp;
import java.util.List;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
public class MyQueries {
private static final Logger log = Logger.getLogger(MyQueries.class.getName());
private final EntityManagerFactory emf = Persistence.createEntityManagerFactory("LegacyDatabasePU");
private final EntityManager em = emf.createEntityManager();
public MyQueries() {
}
public List<Clients> findAll() {
Query q = em.createQuery("select c from Clients c");
List<Clients> clients = q.getResultList();
return clients;
}
public List<Clients> selectWithParameters(Criteria c) {
log.info(c.toString());
String opener = c.getOpener();
String closer1 = c.getCloser1();
String status = c.getStatus();
Query q = em.createQuery(
"SELECT c FROM Clients c "
+ "WHERE c.status like :status "
+ "and c.closer1 like :closer1 "
+ "and c.opener like :opener");
q.setParameter("opener", opener);
q.setParameter("closer1", closer1);
q.setParameter("status", status);
log.info(q.toString());
List<Clients> clients = q.getResultList();
log.fine(clients.toString());
return clients;
}
public Clients findById(int id) {
Clients client = em.find(Clients.class, id);
return client;
}
public void send(int id) {
Clients c = em.find(Clients.class, id);
java.util.Date date = new java.util.Date();
Timestamp t = new Timestamp(date.getTime());
em.getTransaction().begin();
c.setDateUpdated(t.toString());
em.getTransaction().commit();
}
}
In case the parameters are optional, the criteria API provides some more flexibility.
If the selectWithParameters is called often, consider using parameters, since the DB can cache the parametrized query then.
selectWithParameters with optional parameters reads like this:
public List<Clients> selectWithParameters(Criteria criteria) {
log.info(criteria.toString());
String opener = criteria.getOpener();
String closer1 = criteria.getCloser1();
String status = criteria.getStatus();
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Clients> query = criteriaBuilder.createQuery(Clients.class);
Root<Clients> c = query.from(Clients.class);
List<Predicate> wherePredicates = new LinkedList<Predicate>();
if (null != status) {
wherePredicates.add(criteriaBuilder.like(c.get("status"), status));
}
if (null != closer1) {
wherePredicates.add(criteriaBuilder.like(c.get("closer1"), closer1));
}
if (null != opener) {
wherePredicates.add(criteriaBuilder.like(c.get("opener"), opener));
}
query.where(wherePredicates.toArray(new Predicate[0]));
List<Clients> clients = em.createQuery(query).getResultList();
log.fine(clients.toString());
return clients;
}
Thank you, Heiner. This worked, not sure why I had trouble with Heiner's code, but his sample put me in the right direction:
public List<Clients> selectByCriteria(Criteria criteria) {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Clients> clientCriteriaQuery = criteriaBuilder.createQuery(Clients.class);
Root<Clients> clientRoot = clientCriteriaQuery.from(Clients.class);
clientCriteriaQuery.select(clientRoot);
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.phone1), "%" + criteria.getPhone1() + "%"));
if (!criteria.getOpener().equalsIgnoreCase("all")) {
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.opener), "%" + criteria.getOpener() + "%"));
}
if (!criteria.getCloser1().equalsIgnoreCase("all")) {
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.closer1), "%" + criteria.getCloser1() + "%"));
}
if (!criteria.getStatus().equalsIgnoreCase("all")) {
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.status), "%" + criteria.getStatus() + "%"));
}
clientCriteriaQuery.where(predicates.toArray(new Predicate[0]));
List<Clients> clients = em.createQuery(clientCriteriaQuery).getResultList();
return clients;
}
There's probably no substantive difference (?) from what Heiner answered. JPA and JPQL are a bit murky. I can't believe it, but I almost prefer SQL! I'll have to adjust.

Categories