Does Spring Expression Language support IN operator? Similar to SQL IN clause.
public class Security {
private secTyp1;
public Security (String aSecTyp1) {
secTyp1 = aSecTyp1;
}
}
Security security = new Security("BOND");
StandardEvaluationContext context = new StandardEvaluationContext(security);
ExpressionParser parser = new SpelExpressionParser();
// This should return true
boolean result = parser.parseExpression("secTyp1 **IN** {'BOND','SWPI'}").getValue(context, Boolean.class);
// This should return false
result = parser.parseExpression("secTyp1 **IN** {'FUT','SWPI'}").getValue(context, Boolean.class);
I get the following exception
org.springframework.expression.spel.SpelParseException: EL1041E:(pos 8): After parsing a valid expression, there is still more data in the expression: 'IN'
at org.springframework.expression.spel.standard.InternalSpelExpressionParser.doParseExpression(InternalSpelExpressionParser.java:118)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:56)
at org.springframework.expression.spel.standard.SpelExpressionParser.doParseExpression(SpelExpressionParser.java:1)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:66)
at org.springframework.expression.common.TemplateAwareExpressionParser.parseExpression(TemplateAwareExpressionParser.java:56)
at com.rules.AssignableSecurityRule.evaluateInCondition(AssignableSecurityRule.java:48)
at com.rules.AssignableSecurityRuleTest.testINCondition(AssignableSecurityRuleTest.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
It does not support the IN operator because you do not need it. You can use the Method invokation feature instead.
So, invoking .contains(..) on a list will do what you want.
You can try those two solutions
#Test
public void solutionOneTest() {
final Security security = new Security("BOND");
final EvaluationContext context = new StandardEvaluationContext(security);
Boolean contains = PARSER.parseExpression("{'BOND','SWPI'}.contains(#root.secTyp1)").getValue(context, Boolean.class);
Assert.assertTrue(contains);
contains = PARSER.parseExpression("{'FUT','SWPI'}.contains(#root.secTyp1)").getValue(context, Boolean.class);
Assert.assertFalse(contains);
}
#Test
public void solutionTwoTest() {
final Security security = new Security("BOND");
final EvaluationContext context = new StandardEvaluationContext();
context.setVariable("sec", security);
Boolean contains = PARSER.parseExpression("{'BOND','SWPI'}.contains(#sec.secTyp1)").getValue(context, Boolean.class);
Assert.assertTrue(contains);
contains = PARSER.parseExpression("{'FUT','SWPI'}.contains(#sec.secTyp1)").getValue(context, Boolean.class);
Assert.assertFalse(contains);
}
In addition to calling methods directly as in micfra's answer, SpEL also supports some pretty powerful collection filtering expressions as well. Collection Selection allows you to filter out a sublist of matching values, when you could then test for size:
!{'BOND','SWPI'}.?[#this == 'BOND'].empty
Where this is really handy is filtering lists of complex objects:
!securities.?[secTyp1 == 'BOND'].empty
Related
This question already has answers here:
Android Room - simple select query - Cannot access database on the main thread
(23 answers)
Closed 8 months ago.
I´m new to Kotlin and trying to learn it by programming an app for work time recording.
I´ve created a room database which works fine when inserting data. In the next step I would like to retrieve the sum of a column and store this value in a variable. And this is the point where I get stuck. Here are the affected snippets of my code.
data class sumPojo(var sumOvertime: Double)
#Dao
interface OvertimeDao {
#Query(value = "SELECT SUM(overtime) as sumOvertime FROM TableOvertime")
fun getSumOvertime(): sumPojo
}
class OvertimeRepository(private val overtimeDao: OvertimeDao) {
val getSumOvertime: sumPojo = overtimeDao.getSumOvertime()
}
class OvertimeViewModel(application: Application): AndroidViewModel(application) {
private val repository : OvertimeRepository
val getSumOvertime : sumPojo
init {
val overtimeDao = OvertimeDatabase.getDatabase(application).overtimeDao()
repository = OvertimeRepository(overtimeDao)
getSumOvertime = repository.getSumOvertime
}
}
class inputWorktimeFragment : Fragment() {
private lateinit var mOvertimeViewModel : OvertimeViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_input_worktime, container, false)
mOvertimeViewModel = ViewModelProvider(this)[OvertimeViewModel::class.java]
val sumOvertime: sumPojo = mOvertimeViewModel.getSumOvertime
return view
}
}
Following the error message which I receive.
2022-07-06 16:24:03.067 7338-7338/com.example.workingtimerecorder E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.workingtimerecorder, PID: 7338
java.lang.RuntimeException: Cannot create an instance of class com.example.workingtimerecorder.data.OvertimeViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:320)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
at com.example.workingtimerecorder.fragments.inputWorktimeFragment.onCreateView(InputWorktimeFragment.kt:55)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2879)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3129)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2886)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:351)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
at android.app.Activity.performStart(Activity.java:8076)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3660)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:312)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
at com.example.workingtimerecorder.fragments.inputWorktimeFragment.onCreateView(InputWorktimeFragment.kt:55)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2879)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3129)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2886)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:351)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
at android.app.Activity.performStart(Activity.java:8076)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3660)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:469)
at androidx.room.RoomDatabase.query(RoomDatabase.java:525)
at androidx.room.util.DBUtil.query(DBUtil.java:86)
2022-07-06 16:24:03.068 7338-7338/com.example.workingtimerecorder E/AndroidRuntime: at com.example.workingtimerecorder.data.OvertimeDao_Impl.getSumOvertime(OvertimeDao_Impl.java:124)
at com.example.workingtimerecorder.data.OvertimeRepository.<init>(OvertimeRepository.kt:16)
at com.example.workingtimerecorder.data.OvertimeViewModel.<init>(OvertimeViewModel.kt:24)
... 40 more
I would appreciate your help or any hints which could lead me into the rigth direction. I`ve read about several similar issues here on Stackoverflow but none of them could help me.
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
I'd try returning either flow or live data and using view model scope to start a co routine.
in the dao
//change
fun getSumOvertime(): sumPojo
to
fun getSumOvertime(): LiveData<sumPojo>
//or
suspend fun getSumOvertime(): Flow<sumPojo>
in the repo
//livedata
fun getSumPojo():LiveData<sumPojo>{return yourdb.yourdao.getSumovertime}
//flow
suspend fun getSumPojo():Flow<sumPojo>{return yourdb.yourdao.getSumovertime}
in the view model id gain access to the values like such
// create private and public accessors
private var _sumPojo = MutableLiveData<SumPojo> //use Int as mutable type if you do not have an object to map to
val sumPojo: LiveData<SumPojo> // again if you do not have an object for sumPojo use Int as live data type
get() = _sumPojo
private fun refreshSumPojo()=viewModelScope.launch{return yourRepo.getSumPojo()}
init {_sumPojo.value = refreshSumPojo}
in the view I would use databinding and bind the text view to the public accessor
in your view.xml file
<TextView
android:text="#{viewModel.sumPojo.toString()}"
" />
I an enum field in my model,when I use find method to query some data with this field as condition, it return as wanted.
but, when I use bulkOps and excute an upsert operation,it tells me:
can't serialize class com.timanetworks.tpc.vehicle.alarm.domain.enums.FaultType
I am try to add name() method of enum to resolve this problem, and it's success! But I still can not understand why? does any one know?
my model is like this:
#Document(collection = "FaultSnapshot")
#TypeAlias("faultSnapshot")
public class FaultSnapshot extends BaseDocument {
private FaultType type;
private String vin;
private Integer faultLevel;
private Boolean isFault;
private Date time;
...setters and getters...
and FaultType is an enum:
public enum FaultType {
FAULT_EMS,
....
}
and this is find code:
Query query = new Query();
if (type != null) {
query.addCriteria(where("type").is(type));
}
Pageable pageable = new PageRequest(pageIndex - 1, pageSize);
Sort sort = new Sort(Sort.Direction.DESC, "time");
List<FaultHistory> histories = template.find(query.with(pageable).with(sort),
FaultHistory.class);
return new Page<>(count, histories);
this is bulkOps code:
public void upsertSnapshot(Collection<FaultSnapshot> snapshots) {
BulkOperations bulk = template.bulkOps(BulkOperations.BulkMode.UNORDERED, FaultSnapshot.class);
for (FaultSnapshot snapshot : snapshots) {
Query query = new Query();
query.addCriteria(where("vin").is(snapshot.getVin()));
query.addCriteria(where("type").is(snapshot.getType().name()));
Update update = new Update()
.set("isFault", snapshot.getFault())
.set("faultLevel", snapshot.getFaultLevel())
.set("time", snapshot.getTime())
.set("vin", snapshot.getVin())
.set("type", snapshot.getType().name());
bulk.upsert(query, update);
}
bulk.execute();
}
finally,this is the error stack:
Exception in thread "Thread-11" java.lang.IllegalArgumentException: can't serialize class com.timanetworks.tpc.vehicle.alarm.domain.enums.FaultType
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:299)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:194)
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:255)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:194)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:136)
at com.mongodb.DefaultDBEncoder.writeObject(DefaultDBEncoder.java:36)
at com.mongodb.OutMessage.putObject(OutMessage.java:289)
at com.mongodb.OutMessage.writeUpdate(OutMessage.java:180)
at com.mongodb.OutMessage.update(OutMessage.java:60)
at com.mongodb.DBCollectionImpl$Run$1.executeWriteProtocol(DBCollectionImpl.java:908)
at com.mongodb.DBCollectionImpl$Run$RunExecutor.executeWriteProtocol(DBCollectionImpl.java:1025)
at com.mongodb.DBCollectionImpl$Run$RunExecutor.execute(DBCollectionImpl.java:1016)
at com.mongodb.DBCollectionImpl$Run.executeUpdates(DBCollectionImpl.java:917)
at com.mongodb.DBCollectionImpl$Run.execute(DBCollectionImpl.java:859)
at com.mongodb.DBCollectionImpl.executeBulkWriteOperation(DBCollectionImpl.java:169)
at com.mongodb.DBCollection.executeBulkWriteOperation(DBCollection.java:1904)
at com.mongodb.DBCollection.executeBulkWriteOperation(DBCollection.java:1899)
at com.mongodb.BulkWriteOperation.execute(BulkWriteOperation.java:116)
at org.springframework.data.mongodb.core.DefaultBulkOperations.execute(DefaultBulkOperations.java:276)
at com.timanetworks.tpc.vehicle.alarm.dao.FaultSnapshotRepositoryImpl.***upsertSnapshot***(FaultSnapshotRepositoryImpl.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
This is actually a bug (DATAMONGO-1678) in Spring Data MongoDB. It has already been fixed for 1.9.12, 1.10.5 and 2.0.0.RC1.
At the time of writing none of the mentioned versions has been released.
We're using Spring Expression Language (v3.1.2) to validate Object values in a map. The two test cases below outline a scenario where we are asserting the value of a BigDecimal object. The first case passes but the second test fails. I think the way we have defined the expression is correction, but is suspect the implementation is not correctly casting the object value.
#Test
public void testBigDecimalValueLess() {
Map<String,Object> map = new HashMap<String,Object>();
map.put("premiums",new BigDecimal("400000.000000"));
StandardEvaluationContext stdContext = new StandardEvaluationContext();
stdContext.setVariables(map);
// Set expression
String ruleExpression = "#premiums>=new java.math.BigDecimal('500000')";
// Evaluate the SpEL expression
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(ruleExpression);
Boolean returnValue = expression.getValue(stdContext,Boolean.class);
Assert.assertEquals(Boolean.FALSE,returnValue);
}
#Test
public void testBigDecimalValueGreater() {
Map<String,Object> map = new HashMap<String,Object>();
map.put("premiums",new BigDecimal("119000000000.000000"));
StandardEvaluationContext stdContext = new StandardEvaluationContext();
stdContext.setVariables(map);
// Set expression
String ruleExpression = "#premiums>=new java.math.BigDecimal('500000')";
// Evaluate the SpEL expression
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(ruleExpression);
Boolean returnValue = expression.getValue(stdContext,Boolean.class);
Assert.assertEquals(Boolean.TRUE,returnValue);
}
Is there any recommended way to we can achive this on the currect version. Can i pass extra information
to the StandardEvaluationContext object?
EDIT 1
I written this test to show how the BigDecimal values are cast to 'int' and 'long'
#Test
public void bigDecimalIntValue(){
System.out.println(new BigDecimal("1000").intValue());
System.out.println(new BigDecimal("100000").intValue());
System.out.println(new BigDecimal("10000000").intValue());
System.out.println(new BigDecimal("1000000000").intValue());
System.out.println(new BigDecimal("100000000000").intValue()); --> 1215752192
System.out.println(new BigDecimal("100000000000").longValue()); --> 100000000000
System.out.println(new BigDecimal("713027290000.000000").intValue()); --> 62718864
System.out.println(new BigDecimal("713027290000.000000").longValue()); --> 713027290000
}
I suspect i just need to ensure my expression casts both numbers to a Long before the operation is evaluated.
I suggest using compareTo(BigDecimal val) method of BigDecimal class. For details of this method, take a look at http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#compareTo%28java.math.BigDecimal%29 and change your expression to something like this:
String ruleExpression = "#premiums.compareTo(new java.math.BigDecimal('500000')) >= 0";
Suppose I have 2 JPA classes which model 2 entities in datastore (Google app engine) like these:
#Entity
public class Clazz {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key classKey;
#Basic
private String classId;
#Basic
private String className;
#ManyToOne
private Subject subject;
}
#Entity
public class Subject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key subjectKey;
#Basic
private String subjectId;
#Basic
private String subjectName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "subject")
private Set<Clazz> classes = new HashSet<Clazz>();
}
So, how to get Clazz objects which have classId and subjectId equal to given values using JPA criteria. I used this code but got an Exception like this:
em = EMF.get().createEntityManager();
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Clazz> criteriaQuery = criteriaBuilder.createQuery(Clazz.class);
Root<Clazz> root = criteriaQuery.from(Clazz.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (searchObj.getClassId() != null && searchObj.getClassId().length() > 0) {
Expression<String> classIdExpression = root.get("classId");
predicates.add(criteriaBuilder.equal(classIdExpression, searchObj.getClassId()));
}
if (searchObj.getSubjectId() != null && searchObj.getSubjectId().length() > 0) {
Join<Clazz, Subject> join = root.join("subject");
predicates.add(criteriaBuilder.equal(join.get("subjectId"), searchObj.getSubjectId()));
}
if (predicates.isEmpty()) {
criteriaQuery.select(root);
} else {
criteriaQuery.select(root).where(predicates.toArray(new Predicate[predicates.size()]));
}
TypedQuery<Clazz> query = em.createQuery(criteriaQuery);
return query.getResultList();
Exception:
javax.persistence.PersistenceException: SELECT DN_THIS FROM thesis.filesharing.model.Clazz DN_THIS JOIN DN_THIS.subject WHERE (DN_THIS.classId = '44444') AND (DN_THIS.subject.subjectId = 'IT5834'): Can only reference properties of a sub-object if the sub-object is embedded.
at org.datanucleus.api.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:302)
at org.datanucleus.api.jpa.JPAQuery.getResultList(JPAQuery.java:202)
at thesis.filesharing.dao.impl.ClassDAOImpl.countFoundClasses(ClassDAOImpl.java:203)
at thesis.filesharing.bo.impl.ClassBOImpl.countFoundClasses(ClassBOImpl.java:84)
at thesis.filesharing.test.TestController.searchClasses(TestController.java:143)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:115)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:746)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:687)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:915)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:811)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
Thanks in advance
Can only reference properties of a sub-object if the sub-object is
embedded.
is pretty explicit enough. GAE/Datastore can't cope easily with "joining". Whether using JPA Criteria of JPQL is not of relevance, since they equate to the same requirements on the datastore
Actually you should read some about GAE Datastore since datastore based on No-SQL database which is based mainly on data structure there aren't any relationships here (No Tables)
(Only Classes)
#OneToMany..etc not supported any more
From Wikipedia: In computing, NoSQL (commonly interpreted as "not only SQL"[1]) is a broad class of database management systems identified by non-adherence to the widely used relational database management system model. NoSQL databases are not built primarily on tables, and generally do not use SQL for data manipulation.
Since GAE doesn't play well with criteria joins, but accepts statements of the following type:
SELECT f FROM Foo f JOIN f.bar b WHERE b.id = "42"
I managed to implement a hacky solution of this issue using aliases, and it works perfectly. I cannot guarantee the safety of this method though, so use at your own risk.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> q = cb.createQuery(Foo.class);
Root<Foo> foo = q.from(Foo.class);
foo.join("bar").alias("b1"); // Comment 1
foo.alias("b1"); // Comment 2
q.select(foo).where(cb.equal(foo.get("id"), "42"));
foo.alias("f1"); // Comment 3
TypedQuery<Foo> query = em.createQuery(q);
List<Foo> foos = query.getResultList();
Few things to note:
Comment 1: This alias represents only the first b in the JPQL statement.
Comment 2: This alias represents the b in b.id.
Comment 3: This alias represents the f in the JPQL statement
The resulting statement in em.createQuery(q) is equivalent to the JPQL statement above.
I am doing tests on an ejb3-project using ejb3unit http://ejb3unit.sourceforge.net/Session-Bean.html for testing. All my Services long for #PersistenceContext (UnitName=bla). I set up the ejb3unit.properties like this:
ejb3unit_jndi.1.isSessionBean=true
ejb3unit_jndi.1.jndiName=ejb/MyServiceBean
ejb3unit_jndi.1.className=com.company.project.MyServiceBean
everything works with the in-memory-database.
So now i want additionally test another servicebean with #PersistenceContext (UnitName=noTxDatasource) that goes for a defined in my datasources.xml:
<datasources>
<local-tx-datasource>
...
</local-tx-datasource>
<no-tx-datasource>
<jndi-name>noTxDatasource</jndi-name>
<connection-url>...</connection-url>
<driver-class>oracle.jdbc.OracleDriver</driver-class>
<user-name>bla</user-name>
<password>bla</password>
</no-tx-datasource>
</datasources>
How do I tell ejb3unit to make this work:
Object object = InitialContext.doLookup("java:/noTxDatasource");
if (object instanceof DataSource) {
return ((DataSource) object).getConnection();
} else {
return null;
}
Currently it fails saying: javax.NamingException: Cannot find the name (noTxDataSource) in the JNDI tree Current bindings: (ejb/MyServiceBean=com.company.project.MyServiceBean)
How can I add this no-tx-datasource to the jndi bindings?
I hate answering my own questions, but I had some simple thought:
public void setUp() throws Exception {
OracleDataSource ds = new OracleDataSource();
ds.setServerName("localhost");
ds.setPortName(1521);
ds.setDatabaseName("database"); // SID
ds.setUser("user");
ds.setPassword("pass");
InitialContext ic = new InitialContext();
ic.add("noTxDatasource", ds);
}
This will additionally allow you to make the following lookup work:
Object object = InitialContext.doLookup("java:/noTxDatasource");
delivering a datasource (in this case oracle).