I had implement a interceptor of myabtis. but we found a problem, execute interceptor lead to throw so many IllegalAccessException, it affects cpu performence
Shown below is where the problem is, why did not check access permision of feild befor executed code "field.get(target)".
public class GetFieldInvoker implements Invoker {
private final Field field;
public GetFieldInvoker(Field field) {
this.field = field;
}
#Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException {
try {
return field.get(target);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
return field.get(target);
} else {
throw e;
}
}
}
#Override
public Class<?> getType() {
return field.getType();
}
}
the intercepor of mine:
#Intercepts({
#Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class})
})
public class SqlIdInterceptor implements Interceptor {
private static final int MAX_LEN = 256;
private final RoomboxLogger logger = RoomboxLogManager.getLogger();
#Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
String originalSql = boundSql.getSql();
MappedStatement mappedStatement =
(MappedStatement) metaObject.getValue("delegate.mappedStatement");
String id = mappedStatement.getId();
if (id != null) {
int len = id.length();
if (len > MAX_LEN) {
logger.warn("too long id", "id", id, "len", len);
}
}
String newSQL = "# " + id + "\n" + originalSql;
metaObject.setValue("delegate.boundSql.sql", newSQL);
return invocation.proceed();
}
#SuppressWarnings("unchecked")
public static <T> T realTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return realTarget(metaObject.getValue("h.target"));
}
return (T) target;
}
}
Flame Graph
enter image description here
enter image description here
I need help, how to avoid throw exceptions, is any other way to reslove this problem?
thanks.
I am using the latest version of Spring Boot and Spring Data MongoRepository. I have written a custom repository interface
public interface CompanyRepository extends MongoRepository<Company, String>{
#Query(value = "{ 'employer.userId' : ?0 }")
Company findByCompanyUserUserId(String userId);
}
In the same way i want to use #Query annotation for updating a particular field. can someone suggest me?
Create an annotation like this:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface MongoUpdate {
String find() default "{}";
String update() default "{}";
String collection();
boolean multi() default false;
}
And an aspect like this:
#Aspect
#Component
#SuppressWarnings("unchecked")
public class MongoUpdateAspect {
private static final Logger logger = LoggerFactory.getLogger(MongoUpdateAspect.class);
#Autowired
private MongoTemplate mongoTemplate;
#Pointcut("#annotation(com.ofb.commons.aop.common.MongoUpdate)")
public void pointCut() {
}
#Around("com.ofb.commons.aspect.MongoUpdateAspect.pointCut() && #annotation(mongoUpdate)")
public Object applyQueryUpdate(ProceedingJoinPoint joinPoint, MongoUpdate mongoUpdate) throws Throwable {
Object[] args = joinPoint.getArgs();
String findQuery = mongoUpdate.find();
String updateQuery = mongoUpdate.update();
String collection = mongoUpdate.collection();
boolean multiUpdate = mongoUpdate.multi();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Collection) {
Collection collection1 = (Collection) args[i];
String replaceStr = (String) collection1.stream().map(object -> {
if (object instanceof Number) {
return object.toString();
} else {
return String.format("\"%s\"", object.toString());
}
}).collect(Collectors.joining(","));
findQuery = findQuery.replace(String.format("?%s", i), replaceStr);
updateQuery = updateQuery.replace(String.format("?%s", i), replaceStr);
} else if (args[i] instanceof Object[]) {
Object[] objects = (Object[]) args[i];
String replaceStr = Arrays.stream(objects).map(object -> {
if (object instanceof Number) {
return object.toString();
} else {
return String.format("\"%s\"", object.toString());
}
}).collect(Collectors.joining(","));
findQuery = findQuery.replace(String.format("?%s", i), replaceStr);
updateQuery = updateQuery.replace(String.format("?%s", i), replaceStr);
} else {
if (args[i] instanceof Number) {
findQuery = findQuery.replace(String.format("?%s", i), args[i].toString());
updateQuery.replace(String.format("?%s", i), args[i].toString());
} else {
findQuery = findQuery.replace(String.format("?%s", i), String.format("\"%s\"", args[i].toString()));
updateQuery =
updateQuery.replace(String.format("?%s", i), String.format("\"%s\"", args[i].toString()));
}
}
}
Query query = new BasicQuery(findQuery);
Update update = new BasicUpdate(updateQuery);
if (multiUpdate) {
mongoTemplate.updateMulti(query, update, collection);
} else {
mongoTemplate.updateFirst(query, update, collection);
}
return null;
}
}
This will not work in MongoRepository implemented interfaces but you can create an empty bodied method in your service layer
#MongoUpdate(find = {}, update = "{$push : {'offFeatures' : ?0}}", collection = "userPreference", multi = true)
public void offFeatures(String feature) {
}
It's a reasonable question. Assuming that you're using the org.springframework.data.mongodb.repository.MongoRepository class, can you not simply use the insert(..) or save(..) methods for what you need?
API docs
I have the below code snippet for dynamic sorting using JPA Criteria API
Root<Employee> root = criteriaQuery.from(Employee);
Join<Employee, Project> joinProject =
root.join(Employee_.projectList, JoinType.LEFT);
if (sortDirection.equals("asc")) {
criteriaQuery.orderBy(cb.asc(root.get(sortField)));
If I am passing an attribute of Employee entity to order by statement, it works without any hitch, however if an attribute of Project entity is passed to order by statement, exception is thrown stating that
The attribute [projectName] is not present in the managed type
because projectName is an attribute of Projectentity which is joined with Employee using joinProject. In order by statement I am using root.get(sortField). if it is joinProject.get(sortField), it would work fine when attributes of Project are being passed to order by statement.
My questions are
How could I modify my Order By statement in order to cater all the attributes which being passed?
Do I need to conditionally check which attribute and accordingly use if conditions or are there better ways of doing this?
Appreciate insight into this.
A specific approach for a simple scenario (predetermined one-level only joins) may be something like this:
Root<Employee> root = criteriaQuery.from(Employee.class);
Join<Employee, Project> joinProject = root.join(Employee_.projectList, JoinType.LEFT);
Class<?> baseClass = fieldTypeMap.get(sortField);
From<?, ?> from;
if(baseClass == Employee.class)
{
from = root;
}
else if(baseClass == Project.class)
{
from = joinTeam;
}
else ...
Expression<?> expr = from.get(sortField);
if(sortDirection.equals("asc"))
{
criteriaQuery.orderBy(cb.asc(expr));
}
...
where fieldTypeMap is something like:
private final static Map<String, Class<?>> fieldTypeMap = new HashMap<>();
static {
fieldTypeMap.put("employeeName", Employee.class);
fieldTypeMap.put("projectName", Project.class);
...
}
However, this is quick and dirty, ugly and unmaintainable.
If you want a generic approach, things may get a bit complex.
Personally, I'm using my own classes built on top of EntityManager, CriteriaBuilder and Metamodel, which provides dynamic filtering and multi-sorting features.
But something like this should be meaningful enough:
protected static List<Order> buildOrderBy(CriteriaBuilder builder, Root<?> root, List<SortMeta> sortList)
{
List<Order> orderList = new LinkedList<>();
for(SortMeta sortMeta : sortList)
{
String sortField = sortMeta.getSortField();
SortOrder sortOrder = sortMeta.getSortOrder();
if(sortField == null || sortField.isEmpty() || sortOrder == null)
{
continue;
}
Expression<?> expr = getExpression(root, sortField);
if(sortOrder == SortOrder.ASCENDING)
{
orderList.add(builder.asc(expr));
}
else if(sortOrder == SortOrder.DESCENDING)
{
orderList.add(builder.desc(expr));
}
}
return orderList;
}
protected static Expression<?> getExpression(Root<?> root, String sortField)
{
ManagedType<?> managedType = root.getModel();
From<?, Object> from = (From<?, Object>) root;
String[] elements = sortField.split("\\.");
for(String element : elements)
{
Attribute<?, ?> attribute = managedType.getAttribute(element);
if(attribute.getPersistentAttributeType() == PersistentAttributeType.BASIC)
{
return from.get(element);
}
from = from.join(element, JoinType.LEFT);
managedType = EntityUtils.getManagedType(from.getJavaType());
}
return from;
}
This way the join is based on sort field, which is now a dotted-path-expr like "projectList.name" or "office.responsible.age"
public static <X> ManagedType<X> getManagedType(Class<X> clazz)
{
try
{
return getMetamodel().managedType(clazz);
}
catch(IllegalArgumentException e)
{
return null;
}
}
public static Metamodel getMetamodel()
{
return getEntityManagerFactory().getMetamodel();
}
public static EntityManagerFactory getEntityManagerFactory()
{
try
{
return InitialContext.doLookup("java:module/persistence/EntityManagerFactory");
}
catch(NamingException e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
to make it work on a webapp, you have to declare contextual references on web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>my_app_name</display-name>
...
<persistence-context-ref>
<persistence-context-ref-name>java:module/persistence/EntityManager</persistence-context-ref-name>
<persistence-unit-name>my_pu_name</persistence-unit-name>
</persistence-context-ref>
<persistence-unit-ref>
<persistence-unit-ref-name>java:module/persistence/EntityManagerFactory</persistence-unit-ref-name>
<persistence-unit-name>my_pu_name</persistence-unit-name>
</persistence-unit-ref>
</web-app>
update
I don't know about how EclipseLink handles grouping, but also Hibernate does not perform joins (actually, conditions on joins) correctly in some case.
In example, when all these conditions are met:
querying an entity (as Root/From) that is part of a class hierarchy (and not a leaf) based on SINGLE_TABLE
joining/getting a property of a subclass
joining/getting by property name (String) instead of Attribute
To workaround the issue I always resolve property name to an Attribute and reuse joins already "walked" (did I say things may get complicated?):
public class MetaDescriptor extends BusinessObject implements Serializable, MemberEx, ColumnDescriptor
{
private static final long serialVersionUID = 1L;
#BusinessKey
protected final Attribute<?, ?> attribute;
#BusinessKey
protected final MetaDescriptor parent;
protected List<MetaDescriptor> childList;
protected final Type<?> elementType;
...
protected MetaDescriptor(Attribute<?, ?> attribute, MetaDescriptor parent)
{
this.attribute = attribute;
this.parent = parent;
if(attribute instanceof SingularAttribute)
{
SingularAttribute<?, ?> singularAttribute = (SingularAttribute<?, ?>) attribute;
elementType = singularAttribute.getType();
}
else if(attribute instanceof PluralAttribute)
{
PluralAttribute<?, ?, ?> pluralAttribute = (PluralAttribute<?, ?, ?>) attribute;
elementType = pluralAttribute.getElementType();
}
else
{
elementType = null;
}
}
public static MetaDescriptor getDescriptor(ManagedType<?> managedType, String path)
{
return getDescriptor(managedType, path, null);
}
public static MetaDescriptor getDescriptor(From<?, ?> from, String path)
{
if(from instanceof Root)
{
return getDescriptor(((Root<?>) from).getModel(), path);
}
return getDescriptor(from.getJavaType(), path);
}
public static MetaDescriptor getDescriptor(Class<?> clazz, String path)
{
ManagedType<?> managedType = EntityUtils.getManagedType(clazz);
if(managedType == null)
{
return null;
}
return getDescriptor(managedType, path);
}
private static MetaDescriptor getDescriptor(ManagedType<?> managedType, String path, MetaDescriptor parent)
{
if(path == null)
{
return null;
}
Entry<String, String> slice = StringUtilsEx.sliceBefore(path, '.');
String attributeName = slice.getKey();
Attribute<?, ?> attribute;
if("class".equals(attributeName))
{
attribute = new ClassAttribute<>(managedType);
}
else
{
try
{
attribute = managedType.getAttribute(attributeName);
}
catch(IllegalArgumentException e)
{
Class<?> managedClass = managedType.getJavaType();
// take only if it is unique
attribute = StreamEx.of(EntityUtils.getMetamodel().getManagedTypes())
.filter(x -> managedClass.isAssignableFrom(x.getJavaType()))
.flatCollection(ManagedType::getDeclaredAttributes)
.filterBy(Attribute::getName, attributeName)
.limit(2)
.collect(Collectors.reducing((a, b) -> null))
.orElse(null);
if(attribute == null)
{
return null;
}
}
}
MetaDescriptor descriptor = new MetaDescriptor(attribute, parent);
String remainingPath = slice.getValue();
if(remainingPath.isEmpty())
{
return descriptor;
}
Type<?> elementType = descriptor.getElementType();
if(elementType instanceof ManagedType)
{
return getDescriptor((ManagedType<?>) elementType, remainingPath, descriptor);
}
throw new IllegalArgumentException();
}
#Override
public <T> Expression<T> getExpression(CriteriaBuilder builder, From<?, ?> from)
{
From<?, Object> parentFrom = getParentFrom(from);
if(attribute instanceof ClassAttribute)
{
return (Expression<T>) parentFrom.type();
}
if(isSingular())
{
return parentFrom.get((SingularAttribute<Object, T>) attribute);
}
return getJoin(parentFrom, JoinType.LEFT);
}
private <X, T> From<X, T> getParentFrom(From<?, ?> from)
{
return OptionalEx.of(parent)
.map(x -> x.getJoin(from, JoinType.LEFT))
.select(From.class)
.orElse(from);
}
public <X, T> Join<X, T> getJoin(From<?, ?> from, JoinType joinType)
{
From<?, X> parentFrom = getParentFrom(from);
Join<X, T> join = (Join<X, T>) StreamEx.of(parentFrom.getJoins())
.findAny(x -> Objects.equals(x.getAttribute(), attribute))
.orElseGet(() -> buildJoin(parentFrom, joinType));
return join;
}
private <X, T> Join<X, T> buildJoin(From<?, X> from, JoinType joinType)
{
if(isSingular())
{
return from.join((SingularAttribute<X, T>) attribute, joinType);
}
if(isMap())
{
return from.join((MapAttribute<X, ?, T>) attribute, joinType);
}
if(isSet())
{
return from.join((SetAttribute<X, T>) attribute, joinType);
}
if(isList())
{
return from.join((ListAttribute<X, T>) attribute, joinType);
}
if(isCollection())
{
return from.join((CollectionAttribute<X, T>) attribute, joinType);
}
throw new ImpossibleException();
}
public Order buildOrder(CriteriaBuilderEx builder, From<?, ?> from, SortOrder direction)
{
if(direction == null)
{
return null;
}
Expression<?> expr = getExpression(builder, from);
return direction == SortOrder.ASCENDING ? builder.asc(expr) : builder.desc(expr);
}
}
with this construct, I can now safely:
public static List<Order> buildOrderList(CriteriaBuilderEx builder, From<?, ? extends Object> from, List<SortMeta> list)
{
return StreamEx.of(list)
.nonNull()
.map(x -> buildOrder(builder, from, x.getSortField(), x.getSortOrder()))
.nonNull()
.toList();
}
public static Order buildOrder(CriteriaBuilderEx builder, From<?, ? extends Object> from, String path, SortOrder direction)
{
if(path == null || path.isEmpty() || direction == null)
{
return null;
}
MetaDescriptor descriptor = MetaDescriptor.getDescriptor(from, path);
if(descriptor == null)
{
return null;
}
return descriptor.buildOrder(builder, from, direction);
}
If sortField exists only in one entity:
try{
path = root.get(sortField);
}catch (IllegalArgumentException e){
path = joinProject.get(sortField);
}
criteriaQuery.orderBy(cb.asc(path));
I am using Error collector rule in my application( selenium web driver). I am able to thrown exception and continue next line of code with help of error collector rule. But right now i want to re run failed test again ( 3 times) to ensure they are really failed. hence i am using Retry rule. But this rule when applied individually it get executed ( Retry rule with Assert command) `but when written with error collector is doesn't get executed any reason....
Please help me with sample code.
TestBase.java:
public class TestBase {
#Rule
public ErrorCollector collector = new ErrorCollector();
private boolean fatal;
public TestBase() {
fatal=true;
}
public void assertEquals( String msg, Object expected, Object actual) {
if(getFatal()) {
Assert.assertEquals(msg,expected, actual);
} else {
collector.checkThat(msg, actual, CoreMatchers.is(expected));
}
}
public void setFatal(boolean fatalFlag) {
fatal = fatalFlag;
}
public boolean getFatal() {
return fatal;
}
}
BFMNew.java
public class BFMNew extends TestBase {
#Rule
public Retry retry = new Retry(3);
#Rule
public ErrorCollector errocol = new ErrorCollector();
#Before
public void setUp() throws Exception {
System.out.println(" in before");
}
// ===========Re run fail test custom====
public class Retry implements TestRule {
private int retryCount;
public Retry(int retryCount) {
this.retryCount = retryCount;
}
public Statement apply(Statement base, Description description) {
return statement(base, description);
}
private Statement statement(final Statement base,
final Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Throwable caughtThrowable = null;
// implement retry logic here
for (int i = 0; i < retryCount; i++) {
try {
base.evaluate();
return;
} catch (Throwable t) {
caughtThrowable = t;
System.err.println(description.getDisplayName()
+ ": run " + (i + 1) + " failed");
}
}
System.err.println(description.getDisplayName()
+ ": giving up after " + retryCount + " failures");
throw caughtThrowable;
}
};
}
}
#Test
public void one() {
setFatal(false);
Boolean IsLogin = true; //Here function will come for login
Boolean IsPost = null;
Boolean IsStnComment = null;
Boolean IsPhotoUpload = false;
if( IsLogin ) {
IsPost = false;
assertEquals("Failed to Insert Post", true, IsPost);
}
System.out.println(" After Post ");
assertEquals("Failed to upload photo", true, IsPhotoUpload);
if( IsPost ) {
IsStnComment = false;
//assertEquals("Failed to Insert Comment", true, IsStnComment);
}
System.out.println("After comment");
}
The problem is with rules ordering. You should make ErrorCollector to be outer rule and Retry inner rule. Starting from junit 4.10 use this
class YourTest {
private ErrorCollector collector = new ErrorCollector();
private Retry retry = Retry(3);
#Rule
public TestRule chain= RuleChain
.outerRule(collector)
.around(retry);
// tests using collector go here
}
I want to fetch a string from setValues() method of ItemPreparedStatementSetter which is my SQL string. I want to use this String into setSql() method of ItemWriter. Can somebody help me to achieve this.
Below is my PreparedStatementSetter class:
public class PreparedStatementSetter implements
ItemPreparedStatementSetter<Object>{
public static final int INT = 4;
public static final int STRING = 12;
public void setValues(Object item, PreparedStatement ps)
throws SQLException{
#SuppressWarnings({ "rawtypes", "unchecked" })
Map<String, Object> map = (LinkedHashMap) item;
int i = 0;
String columnType;
String sql="";
String final_sql;
try {
sql=generateSql();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
int len=map.size();
for(int k=0 ; k<len ; k++)
{
sql=sql+","+"?";
}
sql=sql+")";
// i want to use this final_sql string in setsql() method of itemwriter
final_sql=sql.replaceFirst("," , " ");
for (Map.Entry<String, Object> entry : map.entrySet()) {
i++;
columnType = entry.getKey().substring(0,
(entry.getKey().indexOf("_")));
switch (Integer.parseInt(columnType)) {
case INT: {
ps.setInt(i, (Integer) (entry.getValue()));
break;
}
case STRING: {
ps.setString(i, (String) (entry.getValue()));
break;
}
}
}
}
private String generateSql()
throws ParserConfigurationException, SAXException, IOException
{
String sql="";
Insert insert;
String table="";
try
{
File is = new File("C:/Users/AMDecalog.Trainees/workspace/SpringJobExecuter/config/input1.xml");
JAXBContext context = JAXBContext.newInstance(Insert.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
insert = (Insert) unmarshaller.unmarshal(is);
Insert in = insert;
List<String> into = in.getInto().getTablename();
for(String s : into)
{
table = table+s;
System.out.println(table);
}
sql = "insert into" + " " + table + " " + "values(";
System.out.println(sql);
}
catch (JAXBException e)
{
e.printStackTrace();
}
return sql;
}
OK, you don't implement your PreparedStatementSetter the right way.
All you have to do is to declare your SQL in the ItemWriter config or in the itemWriter Implementation.
I will assume you are using a JdbcBatchItemWriter:
public class MyItemWrtier extends JdbcBatchItemWriter<MyDomainObj> implements InitializingBean{
#Override
public void afterPropertiesSet() throws Exception {
// set the SQL
String SQL= "UPDATE MYTABLE WHERE FIELD1 = ? AND FIELD2 = ?"
super.setSql(SQL);
}
}
Now, your batch config should declare this writer like this.
<bean id="myItemWriter" class="xxx.yyy.MyItemWriter">
<property name="dataSource" ref="dataSourceIemt" />
<property name="itemPreparedStatementSetter" ref="myPreparedStatementSetter" />
</bean>
And Finally,
#Component("myPreparedStatementSetter")
public class MyPreparedStatementSetter implements ItemPreparedStatementSetter<MyDomainObj> {
public void setValues(MyDomainObj obj, PreparedStatement ps) throws SQLException {
ps.setString(1, obj.getsometing());
ps.setString(2, obj.getsometingElse());
}
}
Hope it is clear.
Regards