There is a way in Spring Data to find if exists a Name that not contains a Code?
Something like:
existsNameWithDifferentCode(String name, Long code);
I'm doing a update and I have to check if this update doesn't contain the same Name as another.
If I cannot do this with the Spring Data signature method, there is a way to do that with ExampleMatcher?
Or any suggestion?
You could use this method name
existsByNameAndCodeNot(String name, Long code);
I recommend to write a JPQL request.
This approach will allow you to more accurately obtain the necessary data from the database without:
thatLongNameForShortRequestWithMultipleConditions()
Related
I am trying to build a REST application in Spring where I have a requirement to delete resources on the basis of certain path variables.
For example, I want to delete resource(s) by id
#DeleteMapping("resources/{id}")
or by name
#DeleteMapping("resources/{name}")
But when I do the above, I get the error
java.lang.IllegalStateException: Ambiguous handler methods
As I understand, Servlets can't tell whether 123 in path /resources/123 represents an ID or a name and hence the ambiguity.
How shall then I design my REST endpoint where a DELETE happens based on some parameter or maybe a combination of parameters?
Spring can't distinguish between the two request as your mapping is ambiguous.
You could try using a query parameter for the second request. So it could look like following :
#DeleteMapping("/hello/{id}")
public String deleteById(#PathVariable("id") Long id) {
return "Delete by id called";
}
#DeleteMapping("/hello")
public String deleteByName(#RequestParam(value = "name") String name) {
return "Delete by name called";
}
Requests like DELETE http://localhost:8080/hello/1 will be handled by deleteById
Requests like DELETE http://localhost:8080/hello?name=deleteMe will be handled by deleteByName.
Or you could add name query param to the same method and if your query param is not null you could delete by name.
For delete by id mapping is fine using path variable
#DeleteMapping("resources/{id}")
For delete by name you can take name as a query param
#DeleteMapping("resources")
public ResponseEntity<?> delete(#RequestParam(value = "name") String name) {
Or you can take both id and name as query param if you want to map both operations in one method in the controller.
Seems like you have two options:
Use only one mapping and disambiguate the request in your controller code. For example, try to parse the path param as an integer or match it to some pattern.
Use regex in the path pattern like #DeleteMapping("resources/{id:[0-9]+}") See Spring Docs - Request Mapping URI Patterns for details. This assumes you can tell the difference between an ID and a name with a pattern though.
I wanted to perform the Spring JPA repository where wanted to apply the and operation among 2 columns where one column cloud have multiple values in it.
SQL query for the same:
select * from table_name where col1='col1_val' and col2 IN
('col2_val_a','col2_val_b','col2_val_c');
I know that for and operation I can extend the JpaRepository and create the method with like this for:
List<MyPoJoObject> findByCol1AndCol2(String col1_val,String col2_val);
and for IN operation we can use : findByCol2In(Collection<String> col2_val)
But i did not know how i can club both the mentioned JPA default method into one, as per my sql statement mentioned before.
You can use the following method named:
List<MyPoJoObject> findByCol1AndCol2In(String col1_val, Collection<String> col2_val);
On this link repository-query-keywords you can find repository query keywords that you can use and combine them as well.
You can certainly combined both into one method.
List<MyPoJoObject> findByCol1AndCol2In(String col1_val,String[] col2_val);
Try this. I am not sure if it will accept Collection<String>. I will try that and update the answer.
HTH.
If you want to perform this logic for more than two columns then your method name becomes verbose.
Instead of stuck with Spring naming why can't you write your own JPA query.
Example:
#Query("select pojo from MyPoJoObject as pojo where pojo.col1 = :col1_val and pojo.col2 in :col2_val")
List<MyPoJoObject> findByColumns(String col1_val, List<String> col2_val);
naive cassandra question, I just want to read the whole column as a string from cassandra using the accessor. Is it possible? Seems to me people are all using data types that are annotated with the table definition :( I could have had defined a user type with the table annotation, but the name of the keyspace is different in every environment
please help. Many thanks
Why not just call .toString () after the mapper ?
Keyspace annotation is not mandatory.
You can specify default keyspace when creating the session.
Session session = cluster.connect("myKeyspace");
PLSQL package-procedure declarations
TYPE custom_type IS TABLE OF single_rec_type;
TYPE single_rec_type IS RECORD(
//id, name etc
)
Problem:
But custom_type has no direct Java type representation [like OracleTypes.CLOB or OracleTypes.CURSOR]
because custom_type is a PLSQL type and not a SQL type.
When I googled, I came across these two options:
To represent it ,
(1) create a SQL TYPE from procedure(or a wrapper PLSQL function) that we can bind from java.
Reference: java - passing array in oracle stored procedure
(2) Register the output parameter with our type and use SQLData object to represent a record.
Reference: Howto get a table as a out parameter in oracle
callableStatement.registerOutParameter(8, OracleTypes.ARRAY, "custom_type");
On doing this, I get the error:
java.sql.SQLException: invalid name pattern: MYDB_OWNER.custom_type
at oracle.jdbc.oracore.OracleTypeADT.initMetadata(OracleTypeADT.java:554)
at oracle.jdbc.oracore.OracleTypeADT.init(OracleTypeADT.java:471)
One suggestion was to declare the custom_type TYPE inside the schema, instead of declaring inside the package.
or by creating public synonym and giving grants.
Question - Regarding the second approach, is it correct practice to declare any custom type in schema level?
Yes, That's the only way it works. I followed the link mentioned in second approach
Howto get a table as a out parameter in oracle
and it worked. The Package level changes included (1) Declaring the custom_type and single_rec_type in schema level [as global, not inside the package] and (2) Replacing IS RECORD with AS OBJECT.
The Java code changes apart from what was mentioned in the link, includes giving the complete name for the class in map.put("SINGLE_REC_TYPE", Class.forName("com.example.SRecord"));
Another thing to notice is that in that example, it mentioned stream.readString();. If you read the API, it says 'Reads the next attribute in the stream and returns it as a String in the Java programming language.' . So if you have three attributes inside the object, then use the method three times like this
id = stream.readString();
name = stream.readString();
designation = stream.readString();
Another point is well mentioned in that post; Regarding the datatypes of attributes inside the object. If there are type mismatch, you get internal representation errors.
eg: correct way:
SRecord.java
public String id; \\varchar in plsql procedure
public String name; \\varchar in plsql procedure
See this related question for Postgres. For some reason, the solution doesn't work for me - the return value of the insert statement is always "1".
See this other question for an XML based solution. I would like to do the same without XML - insert a record and find the new auto-generated id of the record I just insreted.
I didn't find a matching annotation to <selectkey> (see this open issue)
How do I proceed?
Examining mybatis code reveals that INSERT is implemented via UPDATE, and always returns the number of inserted rows! So ... unless I'm completely missing something here, there's no way to do this using the current (3.0.3) implementation.
Actually, it's possible to do it, with the #Options annotation (provided you're using auto_increment or something similar in your database) :
#Insert("insert into table3 (id, name) values(null, #{name})")
#Options(useGeneratedKeys=true, keyProperty="idName")
int insertTable3(SomeBean myBean);
Note that the keyProperty="idName" part is not necessary if the key property in SomeBean is named "id". There's also a keyColumn attribute available, for the rare cases when MyBatis can't find the primary key column by himself. Please also note that by using #Options, you're submitting your method to some default parameters ; it's important to consult the doc (linked below -- page 60 in the current version) !
(Old answer) The (quite recent) #SelectKey annotation can be used for more complex key retrieval (sequences, identity() function...). Here's what the MyBatis 3 User Guide (pdf) offers as examples :
This example shows using the #SelectKey annotation to retrieve a value from a sequence before an
insert:
#Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
#SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);
This example shows using the #SelectKey annotation to retrieve an identity value after an insert:
#Insert("insert into table2 (name) values(#{name})")
#SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);
The <insert>, <update>and <delete> statements return the number of affected rows, as is common with database APIs.
If a new ID is generated for the inserted row, it is reflected in the object you passed as a parameter. So for example, if you call mapper.insert(someObject) inside your annotated insert method, after inserting, you can call someObject.getId (or similar) to retrieve it.
Using the options of <insert>, you can tweak how (by providing an SQL statement) and when (before or after the actual insertion) the id is generated or retrieved, and where in the object it is put.
It may be instructive to use the MyBatis generator to generate classes from a database schema and have a look at how inserts and updates are handled. Specifically, the generator produces "example" classes that are used as temporary containers to pass around data.
you can get your generated ids from save methods,
lets say a bean with ID and name properties,
bean.setName("xxx");
mapper.save(bean);
// here is your id
logger.debug(bean.getID);
I didn't like most of the answers I found online for returning generated keys because
All of the solutions I found called a "setter" on the inbound object
None of the solutions returned the generated column from the method
I came up with the following solution which addresses points 1 & 2 above which
Passes two parameters to mybatis "in" & "out" (mybatis does not mutate "in", it calls a setter on "out")
Requires an additional default method on the interface to return the value
public interface MyMapper {
/**
* this method is used by the mybatis mapper
* I don't call this method directly in my application code
*/
#Insert("INSERT INTO MY_TABLE (FOO) VALUES ({#in.foo})")
#Options(useGeneratedKeys=true, keyColumn="ID", keyProperty = "out.value")
void insert(#Param("in") MyTable in, #Param("out") LongReference out);
/**
* this "default method" is called in my application code and returns the generated id.
*/
default long insert(MyTable tableBean) {
LongReference idReference = new LongReference();
insert(tableBean, idReference);
return idReference.getValue();
}
}
This requires an additional class which can be re-used on similar methods in future
public class LongReference {
private Long value;
// getter & setter
}