Cannot Mix Named and Positional Parameters in Hibernate

Hibernate 4.3 to 5.2 throw QueryException: Cannot mix named and positional parameters when a stored procedure call registers parameters by name and by position. See the message in each version, what Hibernate 7.4 does with mixed HQL and native queries, and the fix.

exceptions-notes

Hibernate throws org.hibernate.QueryException with the message “Cannot mix named and positional parameters” when one stored procedure call registers some parameters by name and others by position. A named parameter is identified by a name such as p_from, whereas a positional parameter is identified by a number such as 2. Hibernate 4.3 to 5.2 print this exact text, while Hibernate 5.3 to 7.4 reject the same call with a reworded IllegalArgumentException and check HQL and native queries in their own way. In every version, the fix is to use one parameter style per call and per query.

We meet the error when we call a stored procedure with StoredProcedureQuery, often after passing the values where the parameter names belong.

The following example shows the error text in both version ranges, followed by a stored procedure call in the wrong and the right way.

java.lang.IllegalArgumentException: org.hibernate.QueryException: Cannot mix named and positional parameters
Caused by: org.hibernate.QueryException: Cannot mix named and positional parameters
java.lang.IllegalArgumentException: Cannot mix positional parameter with named parameter registrations
// Wrong: a name, then a position
StoredProcedureQuery wrong = em.createStoredProcedureQuery("count_departures")
    .registerStoredProcedureParameter("p_from", String.class, ParameterMode.IN)
    .registerStoredProcedureParameter(2, Integer.class, ParameterMode.IN);    // IllegalArgumentException

// Right: names only
Object count = em.createStoredProcedureQuery("count_departures")
    .registerStoredProcedureParameter("p_from", String.class, ParameterMode.IN)
    .registerStoredProcedureParameter("p_hour", Integer.class, ParameterMode.IN)
    .setParameter("p_from", "Delhi")
    .setParameter("p_hour", 7)
    .getSingleResult();                                                      // 2

Notice that the wrong call registers p_from by name and the second parameter by position 2, whereas the right call uses names only.

Next, we see why each Hibernate version reacts differently and how to fix the call and the query.

1. Named and Positional Parameters

A query parameter is a placeholder in the query text, and we set its value with setParameter(). Hibernate sends the value to the database separately from the SQL text. Jakarta Persistence defines two styles.

Named parameterPositional parameter
In the query:from?1
Set withsetParameter(“from”, “Delhi”)setParameter(1, “Delhi”)
In a stored procedure callregisterStoredProcedureParameter(“p_from”, …)registerStoredProcedureParameter(1, …)
ReadabilityThe name says what the value isOnly a number
Native SQL in the JPA specNot defined, Hibernate supports itPortable

Native SQL has a third form, the plain JDBC placeholder ? without a number. Before Hibernate runs a query, it scans the text for parameters and replaces each one with a JDBC ?.

Diagram: the tokens :from (named parameter), ?1 (ordinal parameter) and ? (JDBC-style parameter, native SQL only); colons that are not parameters: '06:00:00' inside a literal, departsAt::varchar, escaped \:\: sent as ::, and :from::varchar read as one parameter named from::varchar; then the rules Hibernate 7.4 applies to HQL and native SQL
Hibernate classifies every : and ? in the query. The check for mixed styles comes after that and differs between HQL and native SQL.

2. Why Hibernate Cannot Mix the Parameters

Our example is a train timetable in an in-memory H2 database with Hibernate 7.4.11 and Java 25. Each TrainService row is one train, and the departure time is a LocalTime.

@Id
@GeneratedValue
private Long id;

private int trainNumber;
private String fromStation;
private String toStation;
private LocalTime departsAt;
trainNumberfromStationtoStationdepartsAt
101DelhiMumbai06:00
102DelhiMumbai16:30
201DelhiJaipur07:15
301MumbaiPune09:45

2.1. Names and Positions in One Stored Procedure Call

The procedure count_departures(p_from, p_hour) counts the trains that leave a station at or after an hour. H2 runs it as a Java method registered with CREATE ALIAS; on MySQL or PostgreSQL it would be a CREATE PROCEDURE. We call stored procedures through StoredProcedureQuery. The first registerStoredProcedureParameter() call fixes the style for the whole call.

A common way to mix the styles is to pass the values where the procedure’s parameter names belong. For example, a timetable screen sends the station and the hour that the user picked, and the developer hands both variables to registerStoredProcedureParameter().

String from = "Delhi";
Integer hour = 7;

StoredProcedureQuery query = em.createStoredProcedureQuery("count_departures")
    .registerStoredProcedureParameter(from, String.class, ParameterMode.IN)    // named parameter "Delhi"
    .registerStoredProcedureParameter(hour, Integer.class, ParameterMode.IN);  // positional parameter 7
// IllegalArgumentException: Cannot mix positional parameter with named parameter registrations

The code compiles because StoredProcedureQuery has one overload that takes a String name and one that takes an int position, so Java unboxes the Integer and picks the int overload.

Step 1: registerStoredProcedureParameter(from, ...) with the String "Delhi" picks the String name overload and registers a named parameter; step 2: registerStoredProcedureParameter(hour, ...) with the Integer 7 is unboxed to int, picks the int position overload, and fails because names and positions are mixed; the fix registers p_from and p_hour and sets the values with setParameter
We meant the variables as values, but Java used them as a name and a position, so the second call mixed the styles.

2.2. Named and Ordinal Parameters in HQL and JPQL

In HQL and JPQL, a numbered parameter such as ?1 is called an ordinal parameter. The Jakarta Persistence specification says “Positional and named parameters must not be mixed in a single query”. However, with default settings, Hibernate 7.4 runs an HQL query that mixes them without an error.

List<TrainService> trains = em.createQuery("from TrainService t where t.fromStation = :from and t.toStation = ?1", TrainService.class)
    .setParameter("from", "Delhi")
    .setParameter(1, "Mumbai")
    .getResultList();                           // trains 101 and 102
// select ... from TrainService ts1_0 where ts1_0.fromStation=? and ts1_0.toStation=?

Hibernate checks the no-mixing rule only in strict JPA query compliance mode, which we turn on with the hibernate.jpa.compliance.query setting, here on HibernatePersistenceConfiguration.

new HibernatePersistenceConfiguration("train-timetable")
    .property(JpaComplianceSettings.JPA_QUERY_COMPLIANCE, true)
    ...

With strict mode on, the same query fails in createQuery(), and the message depends on which style comes first.

java.lang.IllegalArgumentException: org.hibernate.query.sqm.StrictJpaComplianceViolation: Cannot mix ordinal and named parameters
java.lang.IllegalArgumentException: org.hibernate.query.sqm.StrictJpaComplianceViolation: Cannot mix positional and named parameters

2.3. Mixed Styles in Native SQL

For native queries, Hibernate 7.4 rejects only the plain JDBC ? next to another style, and createNativeQuery() throws a ParameterRecognitionException, which is a PersistenceException.

Query query = em.createNativeQuery("select * from TrainService where fromStation = :from and toStation = ?", TrainService.class);
// org.hibernate.engine.query.ParameterRecognitionException: Cannot mix parameter styles between JDBC-style, ordinal and named in the same query

The combination of :from and ?1 runs and returns trains 101 and 102, although Hibernate 5.3 to 5.6 rejected it too, with the message Mixed parameter strategies …. So the behavior has changed between versions in each kind of query.

Grid of Hibernate versions against four cases: a stored procedure call with a name and then a position fails in every version (QueryException in 4.3 to 5.2, IllegalArgumentException from 5.3); HQL with :from and ?1 runs in every version; with strict JPA compliance it runs in 4.3 to 5.2 and fails from 5.3; native SQL with :from and ?1 runs in 4.3 to 5.2, fails in 5.3 to 5.6 and runs again in 6.x and 7.x
Only the stored procedure call fails in every version, whereas whether a mixed query runs depends on the version and its settings.

3. How to Fix It

We pick one style for each stored procedure call and each query, and keep it. Named parameters are the better default, because the name documents the value.

3.1. One Style per Stored Procedure Call

We register the procedure’s own parameter names and pass the values only to setParameter(). Using only positions works as well, as long as we start at 1.

Object count = em.createStoredProcedureQuery("count_departures")
    .registerStoredProcedureParameter("p_from", String.class, ParameterMode.IN)
    .registerStoredProcedureParameter("p_hour", Integer.class, ParameterMode.IN)
    .setParameter("p_from", "Delhi")
    .setParameter("p_hour", 7)
    .getSingleResult();                         // 2
// {call count_departures(?,?)}
Object count = em.createStoredProcedureQuery("count_departures")
    .registerStoredProcedureParameter(1, String.class, ParameterMode.IN)
    .registerStoredProcedureParameter(2, Integer.class, ParameterMode.IN)
    .setParameter(1, "Delhi")
    .setParameter(2, 7)
    .getSingleResult();                         // 2
// {call count_departures(?,?)}

3.2. One Style per Query

A named parameter can appear more than once, so a query never needs a second style.

// Before: :from and ?1
String hql = "from TrainService t where t.fromStation = :from and t.toStation = ?1";

// After: names only
List<TrainService> trains = em.createQuery("from TrainService t where t.fromStation = :from and t.toStation = :to", TrainService.class)
    .setParameter("from", "Delhi")
    .setParameter("to", "Mumbai")
    .getResultList();                           // trains 101 and 102

// One name used twice
List<TrainService> cityTrains = em.createQuery("from TrainService t where t.fromStation = :city or t.toStation = :city", TrainService.class)
    .setParameter("city", "Mumbai")
    .getResultList();                           // trains 101, 102 and 301

A mixed query that runs today still has three problems.

  • It breaks the Jakarta Persistence rule, so it fails as soon as someone turns on strict compliance mode.
  • It depends on the Hibernate version, because the same native query failed on Hibernate 5.3 to 5.6.
  • The Hibernate user guide says “It’s not a good idea to mix named and ordinal parameters in a single query”, so we cannot count on the lenient behavior staying.

When we run the tests with hibernate.jpa.compliance.query set to true, Hibernate finds every mixed HQL query early.

4. Query Parameter FAQs

4.1. Why Does HQL Reject a Question Mark Without a Number?

Because HQL and JPQL in Hibernate 7.4 accept only numbered ordinal parameters, a plain ? fails in createQuery(), alone or next to :from.

java.lang.IllegalArgumentException: org.hibernate.query.ParameterLabelException: Unlabeled ordinal parameter ('?' rather than ?1)

So we write ?1 or use a named parameter instead. In native SQL, a plain ? works when the query uses no other style, and setParameter(1, …) sets the first one.

4.2. Why Does setParameter() Say There Is No Such Parameter?

Because Hibernate checks every name we set against the names it found in the query. A typo fails in setParameter(), whereas a parameter we forget to set fails only when the query runs.

MistakeException and message
setParameter(“station”, “Delhi”) for :fromIllegalArgumentException: No parameter named ‘:station’ in query with named parameters [from]
No setParameter(“from”, …) before getResultList()QueryParameterException: No argument for named parameter ‘:from’

The list in brackets shows the names Hibernate found, which also helps with the colon problem in section 4.4.

4.3. Do Positional Parameters Start at 0 or 1?

They start at 1, both in the query and in setParameter(), so using 0 in either place fails.

MistakeMessage
setParameter(0, “Delhi”) for ?1No parameter labelled ‘?0’ in query with ordinal parameters [1]
?0 in the HQL queryOrdinal parameter labels start from ‘?0’ (ordinal parameters must be labelled from ‘?1’)

4.4. Why Does a PostgreSQL :: Cast Break a Native Query?

Because Hibernate reads the colons right after a parameter as part of the parameter name. PostgreSQL, and H2 too, write a type cast as value::type, so :from::varchar becomes one parameter named from::varchar.

Query query = em.createNativeQuery("select trainNumber from TrainService where fromStation = :from::varchar")
    .setParameter("from", "Delhi");
// IllegalArgumentException: No parameter named ':from' in query with named parameters [from::varchar]

We use cast(:from as varchar), or escape each colon with a backslash (two backslashes in a Java string).

String castQuery = "... where fromStation = cast(:from as varchar)";   // sent: cast(? as varchar), result [101, 102, 201]
String escapedQuery = "... where fromStation = :from\\:\\:varchar";     // sent: ?::varchar, result [101, 102, 201]

A cast after a column, such as departsAt::varchar, and colons inside a string literal such as ’06:00:00′ need no change. A colon directly before a column name does: in H2’s json_object(‘from’:fromStation), Hibernate reads :fromStation as a parameter and fails with No argument for named parameter ‘:fromStation’. Writing ‘from’\\:fromStation in the Java string, or adding a space after the colon, fixes it.

5. Conclusion

Cannot mix named and positional parameters comes from Hibernate 4.3 to 5.2, when one stored procedure call registers parameters both by name and by position, often because we passed values where the names belong. Newer versions reject that call with a reworded message, while Hibernate 7.4 runs HQL and native queries that combine :name and ?1. We still keep one style per call and per query, preferably named parameters, and run the tests with strict JPA compliance turned on.

6. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.